#ding-subscription
1 messages ยท Page 1 of 1 (latest)
Hi there, sorry for the delay.
You could make use of the Destination charge issuing refund API: https://stripe.com/docs/connect/destination-charges#issuing-refunds
By providing the field reverse_transfer=true , the charge will be reversed and payment will return from Connect account.
If you do not wish to refund the entire amount, you can specify the amount to refund to the customer with this extra field amount https://stripe.com/docs/api/refunds/create#create_refund-amount
payment_intent: data.paymentIntentId,
amount: 4825,
reverse_transfer=true
});```
I actually tried this reverse_transfer attr before
and got this error from my backend log
Unhandled error StripeInvalidRequestError: The recipient of this transfer does not have sufficient funds in their Stripe balance to reverse this amount.
which doesnt make sense to me
because when the payment is done, connect account receive some money
why is it saying insufficent fund
If the connected account has a balance that is less than e.g. $43.25 in this scenario, this error would result.
This could be e.g. the amount available for Connect account has already been payout to the user's bank account.
Okay.
I think I come up another solution that might solve my problem but I need a bit of your help
I would recommend debiting amount back to the Connected account in order to allow the reversal to occur successfully.
Sure thing!
I see that
when payment is completed
a transfer is created
And I got that transfer Id from webhook
So I think reverse that transfer solve my probelm
However, I think the transfer is created due to this code
exports.createSubCheckout = functions.https.onCall(async (data, context) => {
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: data.priceId,
quantity: 1,
},
],
subscription_data: {
application_fee_percent: 14.1,
transfer_data: {
destination: 'acct_1KEbRFGIsavKxBch',
},
},
metadata: {
permitId: data.id,
subscribed: data.subscribed,
},
customer_email: data.email,
payment_method_types: ['card'],
mode: 'subscription',
success_url: ${data.url}/my-permits,
cancel_url: ${data.url},
});
return session.id;
});
but in my webhook that listens to transfer.created has empty metadata
๐ฅฒ
Do you know where I can pass metadata so that it shows on the webhook that listens to transfer.created
I thought passing metadata to this checkout session will make it appear in the webhook that listens to transfer.created
If it sounds confusing to you, can i call you through discord to explain it a bit more please ?
This has been bugging me for hours โน๏ธ
Basically what I am saying is, this transfer.created event is not coming from stripe.transfers.create, it is coming from subscribe checkout session, so i don't know where I can pass data to its metadata
I think this transfer event is when Stripe transfer money to the connect account
The metadata set in this scenario would be available in the webhooks related to Checkout, e.g. checkout.session.completed
yea I know
but I want to pass metadata to this transfer event
not the checkout.session.completed
I know I can pass the metadata if i am creating this transfer like this
I know I can pass the metadata if i am creating this transfer like this: const transfer = await stripe.transfers.create({
amount: 400,
currency: 'cad',
destination: 'acct_1KEN5sIiEock6T4N',
transfer_group: 'ORDER_95',
metadata:{blablabbla}
});
but this time the transfer is not coming from stripe.transfers.create
its like an extra event coming from the check out session
I am sorry if this sounds super confusing, but do you get what my problem is now ? ๐
Thank you for the clarification, please let me check if this is doable ๐
thanks
Are you still with me ๐
yes, sorry, I am currently inspecting if the metadata is a part of the subscription_data, would this be reflected in the transfer object's metadata.
Hi there, sorry for the wait. It seems the other metadata field is tied to Subscription.
the transfer metadata ?
I would recommend to listen to the checkout.session.completed event.
If you were to retrieve this Session object [0] and expand by the PaymentIntent object [1], this is where you can see the Charge objects [2], which contains the Transfer Id [3].
[0] https://stripe.com/docs/api/checkout/sessions/retrieve
[1] https://stripe.com/docs/api/checkout/sessions/object#checkout_session_object-payment_intent
[2] https://stripe.com/docs/api/payment_intents/object#payment_intent_object-charges
[3] https://stripe.com/docs/api/charges/object#charge_object-transfer
After obtaining this Transfer id, you can then update the metadata field
let me have a look
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: data.priceId,
quantity: 1,
},
],
subscription_data: {
application_fee_percent: 14.1,
transfer_data: {
destination: 'acct_1KEbRFGIsavKxBch',
},
},
metadata: {
permitId: data.id,
subscribed: data.subscribed,
},
customer_email: data.email,
payment_method_types: ['card'],
mode: 'subscription',
success_url: ${data.url}/my-permits,
cancel_url: ${data.url},
});
return session.id;
});
I am not sure what to do for the first step(retrieve session )
I see this from the doc: const session = await stripe.checkout.sessions.retrieve(
'cs_test_a1FkiPfLi5Ytc61Gvjd1s5w509E99YGigU7Qwn1w6e9b0t7MDyAQwJP9wD'
);
After you have received the checkout.session.completed event, the checkout id should be provided by the event, this is the id you could use to retrieve the Checkout Session object
even I follow the steps, and finally got the transfer id.
But the above lines were already called
where am i going to pass this transfer id
the transfer id should have go in here : metadata: {
permitId: data.id,
subscribed: data.subscribed,
},
I did retrieved the session object as you suggested, the paymentIntent was null
'cs_test_xxx', {
expand: ['payment_intent'],
}
);
const payment_intent = session.payment_intent;
const charges = payment_intent.charges.data
const transfer_id = charges[0].transfer
// Update the Transfer with the metadata
const transfer = await stripe.transfers.update(
'tr_3KFAlEFQX5wP7o0y0XRHtnNn',
{ metadata: {
permitId: xxx,
subscribed: xxx
}
}
);
Is there a request id I could look at please?
ok i think I did it wrong
I dind't do the expand
I was just checking the session object
let me try the above code see if i can get the transfer id!!
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: data.priceId,
quantity: 1,
},
],
metadata: {
permitId: data.id,
subscribed: data.subscribed,
},
customer_email: data.email,
payment_method_types: ['card'],
mode: 'subscription',
success_url: ${data.url}/my-permits,
cancel_url: ${data.url},
});
const retrieveSession = await stripe.checkout.sessions.retrieve(session.id, {
expand: ['payment_intent'],
});
const paymentIntent = retrieveSession.payment_intent;
const charges = paymentIntent.charges.data;
const transferId = charges[0].transfer;
return transferId;
Trying
If I am able to return the correct transferId, then you save my life ๐
Does the code look right to you? I am just waiting it to upload
Unhandled error TypeError: Cannot read property 'charges' of null
const session = await stripe.checkout.sessions.create({
line_items: [
{
price: data.priceId,
quantity: 1,
},
],
metadata: {
permitId: data.id,
subscribed: data.subscribed,
},
customer_email: data.email,
payment_method_types: ['card'],
mode: 'subscription',
success_url: ${data.url}/my-permits,
cancel_url: ${data.url},
});
const retrieveSession = await stripe.checkout.sessions.retrieve(session.id, {
expand: ['payment_intent'],
});
return retrieveSession;
Gonna try this, see what it returns
req_u0ynPqH0c0DGP0
retrieveSession.payment_intent = undefined
check the above requestId
it should show the same, payment intent null
This is because the current payment status is: unpaid, therefore there is no respective PaymentIntent object associated to the Checkout
If you were to try the payment with one of our test cards [0] and try again, that should work ๐
[0] https://stripe.com/docs/testing
ok let me try it again!!
Session id is created when the Checkout Session is created.
As you are creating a subscription, an alternative way would be to have the metadata passed into the Subscription, in this case, it would allow update of metadata ๐
No question is a stupid question, please feel free to ask if there is anything in your mind, I am more than happy to help ๐