Issue
I am trying to implement a PayPal subscription flow where user click on a PayPal subscription button that I have created via the dashboard.
In the back-end, I listen to the PAYMENT.SALE.COMPLETED
webhook that is triggered when a subscription billing is successful. Unfortunately the webhook doesn't send me much infos so that I can retrieve the user and item in my DB linked to the just billed subscription.
This would allow me to securely show private content to that user.
Here is the webhook content sent by payPal (sorry for the length):
const response = {
id: 'WH-4W487015EX264720U-32N35125TV248784B',
event_version: '1.0',
create_time: '2021-04-26T08:24:41.436Z',
resource_type: 'sale',
event_type: 'PAYMENT.SALE.COMPLETED',
summary: 'Payment completed for EUR 6.9 EUR',
resource: {
billing_agreement_id: 'I-T2HP99MJTS1T',
amount: {
total: '6.90',
currency: 'EUR',
details: {
subtotal: '6.90'
}
},
payment_mode: 'INSTANT_TRANSFER',
update_time: '2021-04-26T08:23:59Z',
create_time: '2021-04-26T08:23:59Z',
protection_eligibility_type: 'ITEM_NOT_RECEIVED_ELIGIBLE,UNAUTHORIZED_PAYMENT_ELIGIBLE',
transaction_fee: {
currency: 'EUR',
value: '0.48'
},
protection_eligibility: 'ELIGIBLE',
links: [
{
method: 'GET',
rel: 'self',
href: 'https://api.sandbox.paypal.com/v1/payments/sale/6R7481343K8159132'
},
{
method: 'POST',
rel: 'refund',
href: 'https://api.sandbox.paypal.com/v1/payments/sale/6R7481343K8159132/refund'
}
],
id: '6R7481343K8159132',
state: 'completed',
invoice_number: ''
},
links: [
{
href: 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4W487015EX264720U-32N35125TV248784B',
rel: 'self',
method: 'GET'
},
{
href: 'https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-4W487015EX264720U-32N35125TV248784B/resend',
rel: 'resend',
method: 'POST'
}
],
}
I have tried to GET
the /v1/payments/sale/:id
but it didn't bring me much informations.
I have also checked other stack overflow threads on the subject but it wasn't of any help. I also don't want to use success callbacks provided in the front-end SDK because they are not as secure as a webhook (connection can close before triggering the callback see this gitlab issue)
How can I be aware that a user was billed for his subscription ?
Solution
We finally found a workaround to make our back-end retrieve the buyer and the item.
Front-end
On the subscription button code, we noticed after a lot of trial/errors that the createSubscription
method accept promises and that we could use it to send the subscriptionId the the back-end before the payment continues:
paypal.Buttons({
style: {...},
createSubscription: function (data, actions) {
return actions.subscription.create({
/* Creates the subscription */
plan_id: 'P-26J60279VA924454WMCBPBSA',
}).then(subscriptionId => { // subscriptionId == I-9DH5L3A3JAEB
return new Promise((res, rej) => {
// here we send the subscriptionId to the back-end
// and create a pending subscription
const body = {subscriptionId, userId, itemId};
apiCall('POST', '/subscription', body,() => {
// allow to return subscriptionId to paypal
resolve(subscriptionId);
})
});
});
},
onApprove: function (data, actions) {
// this function was of NO USE
// it is not safe to call your backend here
// as connexion can close and paypal doesn't
// wait after this function to capture payment
// thus leading to orphaned subscriptions
// (paid but not linked to your backend)
},
}).render('#paypal-button');
Back-end (webhook handler)
The back-end wait for the confirmation webhook where webhookResponse.resource.billing_agreement_id
is the subscription id and allow to validate the previously created subscription. I don't exactly understand why billing_agreement_id
is not named subscrition_id
...
Let me know if it's not clear enougth. I let that as an answer until there is a better way to do it :)
Answered By - TOPKAT Answer Checked By - Timothy Miller (PHPFixing Admin)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.