PayPal Docs are just awesome to read. ~Sahil
Quick Links:
- My Notes - Learn Paypal Subscription And Database Schema: Click here
- PayPal Developer Apps and Credentials: Click here
- Sandbox Test Account: Click here
- Official Live Demo's of Paypal Buttons: Click here
- HATEOS Links: Click here
- Link Relations: Click here
- Paypal Story Components: Click here (Tip: Go to Docs Tab to get code for the active story)
- Official Paypal React
@paypal/react-paypal-js: Github - Javasript SDK Reference: Click here
- Official Postman collection by PayPal: Click here and press Run in postman button.
- Sandbox Accounts: Click here
- My Apps and Credential: Click here
- Subscriptions: Click here
- Start a subscription for a future date: Click here
- Integrate Subscritpions: Click here
- Customize Subscriptions to fit into your product UI: [Click here](Customize Subscriptions to fit into your product UI)
- Manage Subscriptions on your account dashboard: Click here
- Billing Cycles: Click here
- Starting a subscription in a future date (and charge when billing cycle actually starts): Click here
- Upgrade or downgrade a subscription (Change subscription plan for a subscription): Click here
- Card Testing: Click here, Card Used in Postman for testing:
"card": {"number": "4012888888881881", "expiry": "2028-03" - For testing with webui: Sample Card:
Card: 4012888888881881 Expiry 03/28 CSC: 888 ZipCode: 95833 City: Sacramento State: California
- How do I accept cards with Checkout using the Guest Checkout option?: Click here
/* eslint-disable @typescript-eslint/no-unused-vars */
// src: https://gist.github.com/romaad/cc588abf691ba1e29c4a853983de8eb1
const axios = require('axios');
const qs = require('qs');
const paypalApi = 'https://api-m.sandbox.paypal.com';
// PLEASE FILL THESE BEFORE RUNNING THE CODE ####
const clientId = '';
const clientSecret = '';
// We need this line to format the body in the expected way
// for 'application/x-www-form-urlencoded'
const payload = qs.stringify({
grant_type: 'client_credentials',
// Note: With `ignoreCache=true` a new token is issued ignoring the previously issued and still not expired token.
ignoreCache: true, // (default=false)
});
const headers = {
Accept: 'application/json',
'Accept-Language': 'en_US',
'content-type': 'application/x-www-form-urlencoded',
};
const auth = {
username: clientId,
password: clientSecret,
};
const config = {
headers, auth,
};
const main = async () => {
try {
const paypalSubscriptionId = 'I-B9YPX4SD1FX3';
const res1 = await axios.post(`${paypalApi}/v1/oauth2/token`, payload, config);
const { data } = await axios.get(`${paypalApi}/v1/billing/subscriptions/${paypalSubscriptionId}`, {
headers: {
Authorization: `Bearer ${res1.data.access_token}`,
},
});
console.log('data?', data);
} catch (error) {
console.log('error?', error.name);
console.log('error?', error.message);
}
};
main();// src: https://gist.github.com/romaad/cc588abf691ba1e29c4a853983de8eb1
const axios = require('axios');
const qs = require('qs');
const paypalApi = 'https://api-m.sandbox.paypal.com';
// PLEASE FILL THESE BEFORE RUNNING THE CODE ####
const clientId = '';
const clientSecret = '';
// We need this line to format the body in the expected way
// for 'application/x-www-form-urlencoded'
const payload = qs.stringify({
grant_type: 'client_credentials',
// Note: With `ignoreCache=true` a new token is issued ignoring the previously issued and still not expired token.
ignoreCache: true, // (default=false)
});
const headers = {
Accept: 'application/json',
'Accept-Language': 'en_US',
'content-type': 'application/x-www-form-urlencoded',
};
const auth = {
username: clientId,
password: clientSecret,
};
const config = {
headers, auth,
};
const main = async () => {
try {
const { data } = await axios.post(`${paypalApi}/v1/oauth2/token`, payload, config);
console.log('data?', data);
} catch (error) {
console.log('error?', error.name);
console.log('error?', error.message);
}
};
main();- Transactions for a subscription won't exist unless the payment has been made, thus if you are fetching transactions for a subscription and it gives you resource doesn't exist erro then that means there the transaction is not successful.
- Also, this stackoverflow answer says txn is created in around 5-10 minutes but in my experience the txn is created on paypal (+ accessible) as soon as the payment is completed.
- Also, when transaction is successful you can see the transaction in Seller Account like that:
We can set start_date and end_date like that -
Link that may be helpful if you haven't seen it (about a monthly renewal timing edge case):
- https://developer.paypal.com/api/nvp-soap/paypal-payments-standard/integration-guide/subscription-billing-cycles/#link-monthlybillingcycles
- "Recurring payments are collected on the same day of the month. If the initial recurring payment falls on the 31st, PayPal eventually adjusts the billing cycle to the last of the month. If the initial recurring payment falls on the 29th or 30th, PayPal adjusts the billing cycle to the first of the month on the following February."
No. It keeps the subscriptionId same always (chatGPT).
We can choose from two options for the PayPal integration i.e,
- using a SDK from PayPal i.e,
- Npm (94k weekly downloads) - https://www.npmjs.com/package/@paypal/react-paypal-js
- Github - https://github.com/paypal/react-paypal-js
OR
- Using HTTP API via our backend. This basically works because PayPal provides a way to redirect the user to a specific "return-url" for e.g., slasher.tv along with subscriptionId, and thus we can make use of that page to help show the user the status of his payment. How does this work?
- User initiates a subscription which is also known as subscription-activation, there are two types:
- a.) using PaPpal login
- b.) using Card Details
Users do this by hitting our slasher api, for e.g, /api/v1/podcast/activate-subscription and behind the scenes we'll call activate-subscription api on paypal server.
-
We get an "approve-url" as result of the "activate-subscription" request and then we return that url to the user.
-
Utilising "approve-url" -
- a.) User navigates to that url and finishes the payment by his PayPal login credentials.
- b) If the user wants to make payment via card user can send card-details as payload for the API call we did in the 1st step, thus the payment will be done right away! (wow!).
- (Note: This step is only for PayPal login payment type only): When user visit the "approve-url", he/she will be prompted to do the txn and will be redirected to a url like below -
https://example.com/app/podcasts/SUBSCRIPTION-RETURN-URL/return?subscription_id=I-VDA50T7LKCFY&ba_token=BA-1Y6871089W3522241&token=1MM66374W7146800L
Thus we can collect subscription_id and check for that subscription status after every 5 seconds while the user is still on this page (it is generally successful instantly though). And we can let the user know the txn is successful by calling a simple txn status or subscription status API on our backend. That's all.
Let me know what your and Damon's preferences are, if possible please try communicating with Damon as these are minute details and don't make much difference after the process is complete.
What are actual differences though in my view in the above two ways?
- (API > SDK) I think with controlled API requests, we would have more control.
- (SDK > API) The npm library seems to be from official PayPal so it can be trusted too.
- (SDK > API) If we go with API requests then we would need to manage card details handling in frontend and which may be a one time thing but it would be reusable for future dating subscriptions too. (high effort task)
- (API > SDK) We would have more control on our frontend UI if we follow the API requests way.
-
More or less, I am equally biased on each way of implementation. Thanks.
-
Also, I tested both ways thoroughly it works fine either way.
Login @ https://www.sandbox.paypal.com/signin
- Seller/Business (note the @business) -
sb-xxxxxxxxxxxxx@business.example.com - Personal/Buyer (note the @personal) -
sb-xxxxxxxxxxxxx@personal.example.com
- Website payment preferences Page in PayPal Dashboard: Click here
Note - You should:
- Go to
Account Settings>Website payments>Website Preferencesand enable bothAuto returnandPayment data transfer. For return url you may givehttps://slasher.tv/app/paypal/return-urlbut it isn't useful as you must definte the return url in eachcreate-subscription(POST /v1//billing/subscriptions) API call. - You may also definte
cancel_urlas well as that would be helpful to know when the payment is failed.
- return url = https://example.com/return
https://example.com/return?subscription_id=I-VDA50T7LKCFY&ba_token=BA-1Y6871089W3522241&token=1MM66374W7146800L
- cancel_url =https://example.com/cancel
https://example.com/cancel?subscription_id=I-7M0U6C9DWEL3&ba_token=BA-0WY1453654212191G&token=4CJ88294AB124721F
- return url = https://example.com/page/subpage
https://example.com/page/subpage?subscription_id=I-7M0U6C9DWEL3&ba_token=BA-0WY1453654212191G&token=8Y12132035380693N
- "cancel_url": "https://example.com/page/subpage"
https://example.com/page/subpage?subscription_id=I-7M0U6C9DWEL3&ba_token=BA-0WY1453654212191G&token=4CJ88294AB124721FDocs - Payment Data Transfer: Click here
onApprove on paypal-react compponent function is only called when txn is approved (i.e., successful)
So, we have either of two options:
- use
Transaction Search > List Transactionin postman PayPal api collection. - use
Subscriptions > Show subscriptions detailsin postman PayPal api collection.
Now, you can use that button to toggle b/w group by product or group by plan view
Date: 29 August, 2023
Clientid:
AUv8rrc_P-EbP2E0mpb49BV7rFt3Usr-vdUZO8VGOnjRehGHBXkSzchr37SYF2GNdQFYSp72jh5QUhzG
ClientSecret:
EMnAWe06ioGtouJs7gLYT9chK9-2jJ--7MKRXpI8FesmY_2Kp-d_7aCqff7M9moEJBvuXoBO4clKtY0v- Calculate sales tax automatically: Click here
- Auto-calculate sales tax: Click here
Account Settings > Account access > API access > Update > Manage REST API apps credentials
Image: 1/2
Image: 2/2
To be able to fetch transaction from my seller account I had to change this setting using my PayPal sandbox developer account**
- Paypal Developer Dashboard: Click here
We can get a list of transactions made for a given subscription by using List transactions for subscription API (we pass a subscription_id as param) in postman. We can use this showing all the payment history for a given person if we store a subscription_id for each user in the database.
Getting the subscription txns and their respective ids from the list of transactions we get by List transactions REST API of postman collection
Source - API DOCS: Click here
Source - Docs: Click here
Other links to help you prorate:
- Stackoverflow Answer: Click here
- Another beautifully written article on Proration and how to do it: Click here
When creating a subscription you can give the start_date to set the start date and time for this subscription. Also, if you just want the subscription to start immeditely you can simply delete that field from the payload of the request and in the response you'll see the current time automatically set by the paypal server.
- You can update different different fields on a subscription but updating
statusfield is not allowed at all. ~IMO Sahil(well tested). - Also, one the plan has been activated you cannot change the
start_date
Please use this site to generate a free url which you can use to test webhook on paypal.
- Webhooks simulator | Paypal: Click here
- Free Webhook testing Tool: Click here
Credit Card Generator, How to use Failed Cards, More 3DS Payments and More Test Accounts.
// Sahil Personal Account (Sandbox Details)
// const clientId = 'AZxr-cT_Indgclkxvqr_yUgFqWiYUJpHLmOKwrucdOseQsFzSrfsCRLaBNZKH9rT5RGCAFK4QgppxKGS'
// const planIds = {
// one: 'P-1U585596S70968643MONXJHI',
// three: 'P-3J697311GS257702PMONXJIQ',
// six: 'P-81C6483981287853TMONXJJQ',
// }























