when I'm trying to make payment with payment request button I need to set 0$ amount when creating payment intent for it, but I can't, cause for creating payment intent stripe requires min 0.50$. I need it cause I'm using it for metered subscription type and don't have to charge client when subscribing. I found solution to refund after successfully subscription, but I don't like it.
I'm creating payment_intent with this api
app.post('/api/client-secret', async (req, res) => {
try {
const { currency, amount } = req.body;
console.log(req.body)
const paymentIntent = await stripe.paymentIntents.create({
amount,
currency: 'usd',
payment_method_types: ['card'],
});
res.json({ clientSecret: paymentIntent.client_secret });
} catch (error) {
console.log("error1", error)
}
});
maybe there is other way or method to create client_secret for stripe.paymentRequest()
The payment_intent object has a field called capture_method. You can set this manual, in which case the payment is not immediately captured by stripe. Pay attention to the parenthesis:
(Not all payment methods support this)
On a different note, if this is a subscription, then why not use the subscription api or even better, sessions. I suggest sessions because it handles all sorts of issues that may arise during a transaction, and all you need to do is wait for the session to complete, and everything will be taken care of.
Also note that the price object (which represents the items you are selling) comes with a field called usage_type which allows you specify that the item is metered, so stripe can handle the billing for you if someone purchases that item.
The stripe api is vast and well documented, so I hope this answer helps you discover a solution quickly.
If you're not charging the user upfront, you should use SetupIntents to save and attach card details to the Customer for charging them in the future — when they’re offline : https://stripe.com/docs/payments/save-and-reuse
Related
Is there a canonical way to get the PaymentIntent ID in order to update a PaymentIntent? (i.e., to maintain security, etc...).
Specifically, all of the documentation and examples I can find of using Payment Element have you set up a payment intent early and only return a client_secret to the client.
export default async function createPaymentIntentHandler(req, res) {
const stripe = new Stripe(STRIPE_SECRET_KEY));
const body = JSON.parse(req.body);
const paymentIntent = await stripe.paymentIntents.create({
currency: 'USD',
amount: 100,
automatic_payment_methods: {
enabled: true,
},
});
res.status(200).send({clientSecret: paymentIntent.client_secret})
}
In order to update that (e.g., if the user changes the quantity on an order) you need the PaymentIntent ID. Now, the PaymentIntent created on the backend already has it, so you could just return it at the same time as the clientSecret:
res.status(200).send({
clientSecret: paymentIntent.client_secret,
pi_ID: paymentIntent.id
})
and have the client send that when hitting the update payment intent API. I guess the main question is, is there any reason not to do that?
Because there seem to be at least two other ways. First, although this seems like it's probably a bad idea, one could just parse it on the server from the client secret. That is, the client secret takes the form <payment_intent_id>_<secret>, so you could just continue sending only the client secret back to the client on the create request, and then extract the id when the client calls the update api with their client secret.
Second, there exists a client-side Stripe API for querying a payment intent. So, when the client wants to update the payment intent, it could
stripe
.retrievePaymentIntent('{PAYMENT_INTENT_CLIENT_SECRET}')
.then(function(result) {
// call update API
});
This latter seems like unnecessary overhead compared to just sending the ID back as part of the original create request, but maybe there's some reason this is actually preferred?
I'm creating a raffle website. The user connects his wallet and pays for a raffle ticket. After the blockchain transaction confirmation, I add his raffle ticket in a collection in firestore.
It causes a security issue because if I allow the user to write to the raffle ticket collection in my firebase security rules, he could create his own tickets without paying.
I need tickets to be added to the database only if payment has been successfully made.
I don't know how websites that have means of payment do it. Maybe firebase isn't a good solution ?
My project is in react/typescript.
You say you do the payment over the blockchain and I assume you use solidity as your smart contract language?
Why don't you emit an event in your smart contract?
You then listen for these events on a (seperate) server.
That updates your (firebase) database whenever an event was emitted.
(Untested) Sample Code:
How do you emit events in solidity? (raffle.sol)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Raffle {
event PaymentCompletion(address buyer, uint256 amountOfTickets);
function buyTickets() external payable {
emit PaymentCompletion(msg.sender, msg.value)
}
}
How do you listen to these events?
when using web3js:
const contract = new web3.eth.Contract(CONTRACT_ABI, CONTRACT_ADDRESS);
const lastBlock = await web3.eth.getBlockNumber()
// paymentEvents is an array containing the payments of the last 500 blocks.
const paymentEvents = await contract.getPastEvents(
'PaymentCompletion', // change if your looking for a different event
{ fromBlock: latestBlock - 500, toBlock: 'latest' }
);
now iterate through these events and put them into your database. You can also set up a subscription which notifies you whenever a new block was created, so you can check if new events were inside of the current block.
This is what it would look like if you add the first blockchain event to the firebase realtime database.
var db = admin.database();
var ref = db.ref("/payments");
// ...
ref.child("path/to/transaction").set({
buyer: paymentEvents[0].buyer,
amountOfTickets: paymentEvents[0].amountOfTickets,
// put the rest of your data here
}, (err) => {
if (err) {
console.error(err)
}
})
Alternatively (if you don't want to handle the payment on the blockchain) you could also take a look at stripe, it also has a firebase plugin for easy integration. (but I've never tried it out). However, imo using the blockchain for handling the payment would be the cleanest solution. (+ you don't have the handling fees stripe uses).
I hope I could give you some good clues! Firebase should be definitely suitable for this.
I'm working on a smart contract that allows users to pay for monthly subscriptions like Netflix, Amazon, etc... with ERC20 tokens.
Everything works well when I tested it. but when I implemented the pay function inside the front-end, so users can interact with the smart contract. the problem that I've faced is that when I clicked on a button to fire off the pay function, I had Metamask asking to confirm the approval, then once the approval is confirmed, I had to confirm the transfer of tokens. **I had to confirm the transfer of tokens. then I got another transaction to confirm the equivalent amount of tokens in ETH.
I made a lot of research but I got stuck, on can I allow the pay function to transfer tokens between two addresses only once.
am I missing something?
PS: blockchain.account & blockchain.smartContract are imported from redux object state.
solidity function
function pay(uint planId) external {
Subscription storage subscription = subscriptions[subscriber][planId];
Plan storage plan = plans[planId];
IERC20 token = IERC20(plan.token);
require(
block.timestamp > subscription.nextPayment,
'not due yet'
);
token.transferFrom(subscriber, plan.merchant, plan.amount);
emit PaymentSent(
msg.sender,
plan.merchant,
plan.amount,
planId,
block.timestamp
);
subscription.nextPayment = subscription.nextPayment + plan.frequency;
}
React function
async function pay() {
showAlert(true, "Happy to see you, Your payment is processing...!");
const data = await blockchain.smartContract.methods.subscriptions(Id).call();
let monthlyPayment = String(data.monthlyPayment);
let tokenAddress = data.amount;
// instance of ERC20 contract
let currency = new web3.eth.Contract(tokenIbi, tokenAddress);
currency.methods.approve("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
amount).send({from: blockchain.account})
.then(
await currency.methods.transfer("0x1b4eAe2DC7Ca0b68643A26177bfC9c069B3D6E04",
amount).send({from:blockchain.account})
)
blockchain.smartContract.methods.pay(Id).send({from: blockchain.account})
.once("error", (err)=> {
console.log(err);
showAlert(true, "Something went wrong...!");
})
.then((receipt)=> {
console.log(receipt);
showAlert(true, "Congratulations, You monthly payment has been submitted successfully");
dispatch(fetchData(blockchain.account));
})
}
Pay button
<button className="btn"
id="launchApp-btn"
onClick={(e)=> {
e.preventDefault();
pay();
}}>
You are making two transaction on each monthly purchase
Approve the allowance of erc20 tokens
Transfer of erc20 token
Each one has its own gas cost. You may multiply the allowance of erc20 on user's first purchase (Which is not fair and brings the security issue) or continue doing the same scenario Approve&Transfer
So this is how I've been told to use paypal react integration on my e-commerce site although I'm not sure if it's the best (I'll tell you later why)
useEffect(() =>{
window.paypal.Buttons({
createOrder: (data, actions, err) => {
return actions.order.create({
intent: "CAPTURE",
purchase_units: [
{
description: 'book',
amount:{
currency_code: 'USD',
value: 100
}
}
]
})
},
onApprove: async (data, action) =>{
completeOrder()
},
onError: err => console.log(err.message)
}).render(buttonsDiv.current)
// console.log(window.paypal)
}, [])
the purpose completeOrder() function is to send a request to the backend in order to verify the order before putting a completed label in the the order
so the problem I'm having with this is completeOrder() runs after the payment was completed and in my experience things like server down or maintenance might occur when the user is paying and that can lead to the user paying for nothing because the order isn't completed
the first solution that I'm looking for is to cancel the payment based on the response from the server. So if the response from the server is an error it will cancel the previous payment. But due to my lack of knowledge is don't see how that's possible
The normal solution is to use the official react-paypal-js for the frontend order approval, and the Checkout-NodeJS-SDK for order creation and capture via API. Server logic to mark an order as completed should be done in the capture route, before returning the capture result JSON to the front-end approval flow.
For a demo of functions that call a server from approval JS, see the server demo. You can implement such client-side functions (createOrder and onApprove) from your react code, and have them call the 2 server routes you need to create.
I'm using NextJS with Firebase, and PayPal is 100x easier to implement client-side. The only worry I have is somebody potentially brute-forcing the amount before the token is sent to PayPal. If I JWT sign with a secret key, is that secure enough (within reason) to dissuade people from attempting to manipulate the prices?
I thought about writing a serverless function, but it would still have to pass the values to the client to finish the transaction (the prices are baked into a statically-generated site). I'm not sure if PayPal's IPN listener is still even a thing, or even the NVP (name-value-pairs). My options as I see them:
Verify the prices and do payment server-side (way more complex)
Sign the prices with a secret key at build time, and reference those prices when sending to PayPal.
I should also mention that I'm completely open to ideas, and in no way think that these are the 'best' as it were.
Pseudo-code:
cart = {
product: [ obj1, obj2, obj3 where obj = { price, sale_price, etc.}],
total: cart.products.length
}
create an order with PayPal, using the cart array, and mapping over values
cart.products.map( prod => { return prod.sale_price || prod.price } etc.
Someone could easily modify the object to make price '.01' instead of '99.99' (for example)