How to Send Custom data fields in PayPal Smart Button transaction - javascript

I am new to PayPal integrations but I have managed to use the client-side JavaScript SDK to create a button and complete a transaction. I have also added a webhook that listens for PAYMENT.CAPTURE.* events and log the return data into my transactions table in my own database. The downside is I do not have a way of tracking for which service or customer the transaction was. So I would like to know how I can add at least one custom field in the button so that it is returned back to me in the webhook POST so that I can perform some business logic for that particular customer.
My initial alternative was to POST the data return onApprove:(data, actions)=>{} but I would have not recovery option if something catastrophic happens before that is done e.g Power outage or general Client-Server connection failure.
Here is my JS basic code for now:
try{
paypal.Buttons({
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: charge.amount,
currency_code:'USD'
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
console.log(details);
if(details.status == "COMPLETED"){
localStorage.clear();
window.location.href = "thank-you";
}
//alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
}
}).render('#paypal-button-container');
}catch(e){
console.error('PayPal not loaded!');
}

Switch to a proper client-server integration.
Follow the PayPal Checkout integration guide and make 2 routes on your server, one for 'Create Order' and one for 'Capture Order' (see the optional step 5 in 'Add and modify the code'). Both of these routes should return only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, which is the PayPal transaction ID) and perform any necessary business logic, such as saving additional data from form inputs.
(That data can be transfered as part of the fetch capture call by adding a body key to its options parameter, which can serialize a JSON object for you. Your 2nd server route can then parse that input as JSON and verify it is ok before proceeding with the capture.)
--
Pair those 2 routes with the frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server
With the above method, you have an immediate, synchronous API response on payment capture. There is no need for an additional asynchronous notification from webhooks, so those will basically become superfluous to you.

You can switch to a client-server integration (as per #Preston PHK's answer), but for that may be overkill for some applications (and not what the OP asked for).
For a simple purchase where you just need to pass some specific information, you can try leveraging the purchase_units.invoice_id field (assuming you don't need that for something else).
The caveat is that the invoice_id needs to be unique every time you use it. This means that you can't just use your customer's internal id. You would need to add something extra to ensure uniqueness.
Your createOrder call might then look something like this:
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
invoice_id: customerId + "-" + randString,
amount: {
value: charge.amount,
currency_code:'USD'
}
}]
});
},
A 6 character random string of alphanumeric (upper and lower) characters gives over 56 billion possibilities, which for most purposes should provide sufficient uniqueness. If you want to be even safer, make it longer. I'm not sure of the exact limit on invoice_id but I think it's at least 40 characters.
When you process the transaction (from a webhook) on your server, you just throw away the extra characters.
$custId = substr($invoice_id, 0, -7);

You can add 'custom_id' field in creareOrder function like that :
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
custom_id: customValue,
amount: {
value: price
}
}]
});
},

Related

PayPal Donate SDK - Prefill Amount and Frequency

I am building an application which includes a custom donation form where users can select:
Frequency: monthly, single
Amount: 3 x Buttons with values, input for custom value
I am using the PayPal Donate SDK and cannot find a way to pass across the amount or frequency so that the amount is pre-filled and the "make this a monthly donation" checkbox selected/deselected based on the users input.
The PayPal button has been added to the page (just absolute styled over the form button) and the SDK opens the donation page in a modal which is my desired result.
This is the code to display my button:
window.PayPal.Donation.Button({
env: "sandbox",
hosted_button_id: "xxxxxxxxxxxx",
onComplete: function() {
// Redirect to success page, save the PP transaction details, etc.
}
}).render("#paypal-donate-button-container");
With the standard PayPal.Buttons API, I am able to pass in a createOrder function like so, where I can set the amount, for example;
window.PayPal.Buttons({
// ...
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: document.querySelector(".selected-premium").dataset.value
},
reference_id: document.querySelector(".selected-premium").dataset.days + "-days"
}]
});
}
}
Is there a way to pass through the amount and frequency using the SDK, similar to how I can do it with the standard payments API? If not, what are the alternatives in 2022 that don't rely on SO answers that use docs that no longer exist/deprecated methods?
With the PayPal Donations flow (this includes the Donate SDK)
You can pass a parameter for the amount and currency_code. If you do pass a prefilled amount, an option to make the donation recurring will not be available.
You cannot pre select the option to make the donation recurring in the Donations flow.
For the case of recurring donations -- since it's not possible to precheck the option to make them recurring in the Donations flow, nor is it possible to prefill the amount and have that choice of recurrence even be available -- what you could do is instead use a Subscription flow for when a recurrence is selected, and have this render in a separate div container that is shown/hidden based on your monthly vs single selection. A subscribe button for your account can be generated at: https://www.paypal.com/billing/plans . The plan it uses must have a specific amount, but this can be overridden in the JS createSubscription function (based on whatever selection or amount was entered on your site) by (in addition to the base plan_id) adding a plan object that contains a billing_cycles object with the amount/frequency you want.

nodeJS Paypal REST API to revise plan give broken but working link

I'm trying to integrate Paypal into my app in NodeJS. I'm using REST API since the official npm packet is deprecated.
Let's suppose I have a product with two plans, planA and planB, necessary for recurring payments like subscriptions. Suppose a customer subscribe to planA, which costs 10$. After a while, he wants to switch to planB, which costs 20$, to unlock premium content in the platform.
I found the API: POST/v1/billing/subscriptions/{id}/revise
with which one should be able to send the planID planB to switch to it. You can also send effective_time field to specify when the change is effective. After calling this API, Paypal reply with 6 links, and I use the first (approve) to redirect the customer to Paypal domain to confirm it's will to switch the plan. After the user login, confirm and click "Accept and subscribe" to the new plan, the page always give me the following error: Things don't appear to be working at the moment. Please try again later.
, despite the plan change goes fine (I can verify it through dashboard).
I'm wondering what can I do to avoid that error.
I want to clarify that in the settings, through the dashboard, under Account settings -> Website payments -> Website preferences, I temporarily have the option Block non-encrypted website payment to Off.
Thank you all in advance!
The setting "Block non-encrypted website payment" is not relevant to this issue, and will have no effect. It applies exclusively to legacy HTML-only payments, which you should not concern yourself with.
Edit: ah yes, a redirect integration requires an application_context with a return_url. For usage with the SDK, no redirect_url is used, hence why the field is not required by the API.
Previous answer follows:
The issue you describe seems to be a problem with the PayPal site, and possibly only occurs in sandbox mode or with certain browsers/cookies. You can test as desired and contact PayPal's support if needed.
It is also possible to do a revise with the JS SDK rather than a redirect. For a client-side-only integration (no API), this can be done using actions.subscription.revise. Search for that text within the SDK reference.
To combine the JS SDK with the API call you are using, have your button code fetch the revised subscription ID from your server. Here is a sample for a create, which you can adapt to be a revise as it's essentially the same thing (you'd just likely be using a /revise endpoint /path/on/your/server)
<script src="https://www.paypal.com/sdk/js?client-id=..........&vault=true&intent=subscription"></script>
<div id="paypal-button-container"></div>
<script>
paypal.Buttons({
style: {
label:'subscribe' //Optional text in button
},
createSubscription: function(data, actions) {
return fetch('/path/on/your/server/paypal/subscription/create/', {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(serverData) {
console.log(serverData);
return serverData.id;
});
},
onApprove: function(data, actions) {
/* Optional: At this point, notify your server of the activated subscription...
fetch('/path/on/your/server/paypal/subscription/activated/' + data.subscriptionID , {
method: 'post'
}).then(function(res) {
return res.json();
}).then(function(serverData) {
//
});
*/
//You could additionally subscribe to a webhook for the BILLING.SUBSCRIPTION.ACTIVATED event (just in case), as well as other future subscription events
//Ref: https://developer.paypal.com/docs/api-basics/notifications/webhooks/event-names/#subscriptions
// Show a message to the buyer, or redirect to a success page
alert('You successfully subscribed! ' + data.subscriptionID);
}
}).render('#paypal-button-container');
</script>

stripe: Create a 30-day trial subscription charge on a saved card but allow user to upgrade and start charging immediately?

So before a user can create an account I want to save their credit card to charge a subscription with 30 day trial AND the ability to immediately charge the card for a subscription if the user demands it.
so my logic is to
1) create a customer
2) add payment details for customer
3) create subscription with 30 day trial
4) activate subscription upon user upgrade action
I'm not clear on how 4) is possible. I get that on 3), after 30 days, they are on a subscription. but what if the customer wants to start using the full version immediately before the trial is over, how would I create a charge for the subscription?
const stripe = require('stripe')('sk_test_asdfasdf');
(async () => {
// Create a Customer:
stripe.customers.create({
email: 'jenny.rosen#example.com',
payment_method: 'pm_1FWS6ZClCIKljWvsVCvkdyWg',
invoice_settings: {
default_payment_method: 'pm_1FWS6ZClCIKljWvsVCvkdyWg',
},
}, function(err, customer) {
// asynchronously called
});
//create subscription
stripe.subscriptions.create({
customer: 'cus_4fdAW5ftNQow1a',
items: [
{
plan: 'plan_CBXbz9i7AIOTzr',
},
],
expand: ['latest_invoice.payment_intent'],
}, function(err, subscription) {
// asynchronously called
}
);
})();
I'll chime in on this really quick, to hopefully get you in the right direction; This sounds like a case for Setup Intents. You can collect payment details, with the intent to charge at a later time. Since the trial won't incur any charges at first, all is good. However you work out the logic to switch from trial to active status on the subscription, you'd update the subscription end-date to end the trial.
This is nicely summarized here, for the most part, save for updating the Subscription and setting the trial_end argument:
https://stripe.com/docs/payments/save-and-reuse
API docs entry on updating the Subscription:
https://stripe.com/docs/api/subscriptions/update#update_subscription-trial_end
Once the trial is over, whether "naturally" or by explicitly setting the end timestamp, an invoice should be sent out or default payment method charged, depending on your settings. It wouldn't hurt to work in some good flow here, concerning user-experience; For example, having a confirmation step to let the customer know they are about to be charged X amount, before actually firing off that off.
Here are other helpful docs:
https://stripe.com/docs/saving-cards
https://stripe.com/docs/payments/setup-intents
https://stripe.com/docs/billing/subscriptions/trials
https://stripe.com/docs/billing/subscriptions/payment#handling-trial
https://stripe.com/docs/api/subscriptions/update

Using Negative Testing via Paypal Express Checkout client-side JS button implementation

I'm currently working on a PayPal Express checkout integration using the Client-side JS approach for taking payments. I'm looking to utilise their "Negative Testing" feature to try to simulate potential errors and provide appropriate responses to the customer.
Just a reference to the relevant doc page here for reference
It seems to enable negative testing you need to pass an extra header along with the the payment request specifying the particular error you would like to trigger for that payment.
This is my current JS for setting up the transaction:
<script>
//We need to convert our generated json string into a bonified javascript object
var paypal_transaction = JSON.parse(JSON.stringify(<?php echo $paypal_json; ?>));
paypal.Button.render({
env: 'sandbox', // 'production'/'sandbox',
commit: true, // Show a 'Pay Now' button - Allows us to capture the payment right away
client: {
sandbox: 'Ab_hPp7h70DoFKChLMSynNxacQQbGcb_tP1cDbzW9jC6a0rYIZH0CkEYYfkw6csvmlyTmfLnagelqB85',
production:''
},
//How we want the button to look
style: {
size: 'responsive',
color: 'gold',
shape: 'rect',
label: 'pay'
},
headers: {
'{"mock_application_codes":"INSTRUMENT_DECLINED"}'
}
payment: function(data,actions) {
return actions.payment.create({
//Pass our created transaction to paypal.
payment:paypal_transaction,
/**
* We have to set the following fields to prevent the client from
* changing their delivery address when they're at PayPal
*/
experience: {
input_fields: {
no_shipping: 0,
address_override:1
},
}
});
},
onAuthorize: function(data, actions) {
/**
* [description]
* #param payment - The authorised transaction returned from paypal
* #return redirect - We redirect the cutomer to our confirmation page as soon as we return from PayPal as we're confident we have the correct
*/
return actions.payment.execute().then(function(payment) {
actions.redirect();
});
},
onError: function(err) {
console.log(err);
// Show an error page here, when an error occurs
},
onCancel: function(data, actions) {
return actions.redirect();
// Show a cancel page or return to cart
}
}, '#paypal-button');
Essentially my question is where do I specify the mock application codes like this in the above implementation.
In the docs they give an example cURL request with the below as the extra header that needs to be passed:
"PayPal-Mock-Response:{\"mock_application_codes\":\"INSTRUMENT_DECLINED\"}"
I just don't know how to do this via the JS approach. Can negative testing only be used with a server side implementation?
Hope that's all clear enough!
Had similar issue myself, and the official answer I got was that it is not available:
"I understand this is a frustrating situation. Unfortunately we do not
have any way to offer negative testing for client side integrations.
It may possible for you to do so using Postman, however, we do not
have documentation to offer."
This is really sad though, other payment providers have fixed card numbers for certain error scenarios for example, or have special payment value based codes. PayPal only has that for the Payflow integration, and the request header based mocking stuff is also only possible if you are directly calling their REST APIs.
PayPal is really lame in these aspects, as even if you are mocking behavior with server integration (not that hard, for this at least they have proper code examples), this mocking is explicit and you control the error. If it would be implicit, and originate from an actually validated but invalid card for example, it would be more realistic.

how to create transaction without plan in braintree js+python?(whiteout subscription)

I am using braintree payment gateway in my application.
i am able to create the transactions with selecting the plan, but what i needed is to create the transaction without selecting any plan. One time payment.
my code
create_sub = braintree.Subscription.create({
"payment_method_token": the_token,
"plan_id": PLAN_ID
})
here subscription is created.
payment_method_result = braintree.PaymentMethod.create({
"customer_id": merchant_customer_id,
"payment_method_nonce": nonce,
"options": {
"make_default": True
}
})
here payment_method is got created
here what i want is to create transaction directly without subscribing.
and save all transaction related data to transaction model.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
You can create a one-time transaction with Braintree's Python API library using the following call:
result = braintree.Transaction.sale({
"amount": "10.00",
"payment_method_token": the_token,
"options": {
"submit_for_settlement": True
}
})
This creates a single transaction with no affiliation to any plan. In place of the "payment_method_token", you may also use "payment_method_nonce", passing in the nonce received from your client. You can find the full list of available parameters in Braintree's API Documentation.

Categories