PayPal Subscription Button Coupons - paypal

I am using Paypal buttons to subscribe customers to a plan.
I am trying to apply coupons on the Paypal buttons without any luck.
This is the code that generate the Paypal button on the frontend:
paypal.Buttons({
style: {
shape: 'pill',
color: 'blue',
layout: 'vertical',
label: 'subscribe'
},
createSubscription: function (data, actions) {
return actions.subscription.create({
"plan_id": "<PAYPAL_PLAN_ID>"
});
},
onApprove: function (data, actions) {
console.log('success');
}
}).render('#paypal-button-container');
On the documentation I saw that I need to define a javascript variable and I tried
var discnt=10;
And:
var discount_amount=10;
None of this variables are working.
Even if the discount will work this way, I want to give the marketing team to define Coupons on their own, and give it to the customers.
Anyone might know how to add coupons to paypal.Buttons?

In addition to the plan_id , you can pass a plan object which overrides the provided plan's details with a different amount, and with a description that reflects the text "discount" or "coupon applied" somewhere.
Parameters for a subscription create call are documented here: https://developer.paypal.com/api/subscriptions/v1/#subscriptions-create-request-body
So for instance, something like
return actions.subscription.create({
"plan_id": "<PAYPAL_PLAN_ID>",
"plan" : {
"name" : "My Service - $10 coupon",
"billing_cycles" {
/* New billing cycles object with lower amount */
}
}
}

Related

PayPal create_order_error cannot read properties of null

I've integrated PayPal into my .NET 6 (Blazor) application. I followed the PayPal docs here to add the button. In test mode all worked fine. Now that I've changed the clientid to live I get an error:
create_order_error Cannot read properties of null
I can see the token is null, but I'm not sure what to do about it?
The code I'm using to create the order is here:
paypal.Buttons({
// Style the button
style: {
...
},
// Called when the button is clicked
onClick: function (data, actions) {
...
},
// Sets up the transaction when a payment button is clicked
createOrder: function (data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: document.getElementById('p-amount').value,
}
}]
});
},
// Finalize the transaction after payer approval
onApprove: function (data, actions) {
...
},
onError: function () {
...
}
}).render('#paypal-button-container');
I have searched for this error but it didn't generate any results. Any help on how to resolve this is much appreciated.
document.getElementById('p-amount')
Is null. Make it be something that's not null.

Sales tax not updated when shipping option is changed if buyer pays with Credit/Debit rather than PayPal?

In the PayPal JavaScript SDK when the order is created the client passes the shipping cost options. If the purchaser changes the shipping option from within the PayPal window, the sales tax must be recalculated because sales tax applies to shipping for this product. The sales tax is not passed in the order creation but the onShippingChange event is fired/invoked after the address is first applied in PayPal; and then the new values, including sales tax, are passed through return actions.order.patch().
If the purchaser pays through clicking the PayPal button, all works fine because the onShippingChange event is fired/invoked when the shipping option is changed. However, when the purchaser uses the Credit/Debit button to pay, the onShippingChange event is fired/invoked only once and not again when the shipping option is changed. If the user changes the shipping option, the cost appears to be taken from the data passed in the order creation and since the onShippingEvent is not run again, the sales tax remains based upon the initial shipping option.
The only difference in these two scenarios is which button the purchaser uses to pay: either PayPal for an account, or Credit/Debit to pay as a guest. All the JS code is the same.
Am I doing something wrong that is causing the onShippingChange event code to not be invoked? Thank you.
A couple things I've noticed while working on this. One is that the messages of "contacting merchant" and "processing" vary, such that when the shipping option is changed when paying with credit/debit, the message is only "processing". When paying with PayPal the message is "contacting merchant." Another is that even after the values are updated, with the sales tax inaccurate, the total cost shown in the top right corner of the PayPal window differs from the total in larger font in the message by difference in shipping options. The one in large font never changes when the shipping option is changed.
In the image below, the shipping option was changed from $9.25 priority to $4.00 standard. The initial total was $28.83. When shipping was changed to standard, the cost changed as $(28.83-9.25+4.00) = $23.58. Sales tax was not adjusted; and it should be $23.27.
I asked a similar question a day or so ago, but thought the values were correct in both payment scenarios but now see they are not.
Update:
After messing around with various options it occurred to me that I don't need to provide shipping options in the PayPal window. If the buyer wants to change the option, he'll have to cancel and change the order in the web page. That eliminates the issue of onShippingChange not firing. Not ideal; but good enough for our little case, especially since few buyers will be changing their shipping option in PayPal, for they see all that cost info. before clicking a payment button.
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
reference_id: "HardCopy_0001",
amount: {
value: order.discSub.toFixed(2)
},
shipping: {
options: [
{
id: "STD",
label: "USPS Standard",
type: "SHIPPING",
selected: order.el_shipOptsInp[0].checked,
amount: {
value: order.shipStdVal.toFixed(2),
currency_code: "USD"
}
},
{
id: "PRI",
label: "USPS Priority",
type: "SHIPPING",
selected: order.el_shipOptsInp[1].checked,
amount: {
value: order.shipPriVal.toFixed(2),
currency_code: "USD"
}
}
]
}
}],
});
},
// Finalize the transaction after payer approval
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
/* ... */
});
},
onShippingChange: function (data, actions) {
try {
// Reject non-US addresses
if (data.shipping_address.country_code !== 'US') {
return actions.reject();
}
const taxRate = data.shipping_address.state === 'PA' ? order.taxRate : 0,
shippingAmount = parseFloat(data.selected_shipping_option.amount.value),
tax_total = taxRate === 0 ? 0 : Math.round( (order.discSub + shippingAmount) * order.taxRate * 100)/100,
oTotal = order.discSub + shippingAmount + tax_total;
if ( data.selected_shipping_option.id === "STD" ) {
if ( !order.el_shipOptsInp[0].checked ) {
order.el_shipOptsInp[0].checked = true;
order.change();
}
} else if ( !order.el_shipOptsInp[1].checked ) {
order.el_shipOptsInp[1].checked = true;
order.change();
}
return actions.order.patch([{
op: "replace",
//path: "/purchase_units/#reference_id==\'default\'/amount",
path: "/purchase_units/#reference_id=='HardCopy_0001'/amount",
value: {
value: oTotal.toFixed(2),
currency_code: "USD",
breakdown: {
item_total: {
currency_code: "USD",
value: order.discSub.toFixed(2)
},
shipping: {
currency_code: "USD",
value: shippingAmount.toFixed(2)
},
tax_total: {
currency_code: "USD",
value: tax_total.toFixed(2)
}
}
}
}
]);
} catch (err) {
console.log("We apologize. There was an error in the onShippingChange event. Reason is provided below.");
console.log(err);
order.error();
return actions.reject();
}
},
onError: function(err) {
console.log(err);
// For example, redirect to a specific error page
//window.location.href = "/your-error-page-here";
order.error();
}
}).render('#paypal-button-container');

How to customise the amount of a Paypal subscription made using JS SDK

I'm trying to upgrade a legacy system that uses Paypal for subscriptions. The older way redirects to Paypal (Checkout?) and the new way is trying to use their JavaScript SDK:
https://developer.paypal.com/sdk/js/reference/
return actions.subscription.create({
'plan_id': 'P-2UF78835G6983425GLSM44MA'
});
The older system created Payment Plans on the fly, and so the payer was able to adjust the amount rather than sticking with the amount stored in a pre-made plan. I hope to replicate this behaviour using the JS sdk.
The above pages links to here: https://developer.paypal.com/api/subscriptions/v1/#subscriptions_create "for allowed options defined in the request body."
One of the parameters within the subscription creation is:
plan [object]
"An inline plan object to customise the subscription. You can override plan level default attributes by providing customised values for the subscription in this object."
This seems like the thing I am after but I am unable to get Papal to use anything I put inside there, ie:
plan_id: plan_id,
custom_id: "Does this thing work?",
plan_overridden: true,
plan: {name: "Testing ABC123 Special"}
"Does this thing work?" will carry but nothing within the plan.
One other thing of note is that the older system didn't pass a plan_id, which doesn't seem possible with the JS SDK.
I think the older way is using Checkout, are these two methods completely imcompatible?
Thank you so much!
name cannot be overridden.
Keys that can be overridden are documented in the plan_override object.
If you need to customize more plan details, create the subscription on the server side and return its id to the calling JS (within that server route, the plan_id can first be created on the fly if one doesn't already exist for the name you need)
<script src="https://www.paypal.com/sdk/js?client-id=XXXXXXXXXXXXX&vault=true&intent=subscription&currency=USD"></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>
My answer was found on this page:
https://developer.paypal.com/docs/multiparty/subscriptions/customize/bill/
billing_cycles: [
{
sequence: 1,
total_cycles: 0,
pricing_scheme: {
fixed_price: {
value: "8",
currency_code: "USD",
},
},
},
],
It didn't make the name change but it did make the amount change which is actually all I care about :)

How to reference a paypal webhook to a user?

I'm trying to figure out the best way to associate the webhook to a user of the webapp.
Upon a paypal transaction I receive the webhook from paypal
but it doesn't have a reliable reference that I can use
to associate with the user in the app.
My solution would be to:
On the onApprove function use javascript code to submit a form with the subscriptionID
hidden on an input and send it to the server.
After receiving the subscriptionID on the server, create the webhook database entry with the subscriptionID field of the user that made the transaction.
And when I receive the webhook from paypal I can reference the user through the
subcriptionID.
Anyone around went through this one with a better option?
I was thinking if there is a way to insert the userID of the webapp in the paypal webhook.
Would be more clean.
Havent' found anything tho.
Here is the paypal webhook client call:
<script>
paypal.Buttons({
style: {
shape: 'pill',
color: 'gold',
layout: 'vertical',
label: 'subscribe'
},
createSubscription: function(data, actions) {
return actions.subscription.create({
/* Creates the subscription */
plan_id: '************************'
});
},
onApprove: function(data, actions) {
alert(data.subscriptionID); // You can add optional success message for the subscriber here
}
}).render('#paypal-button-container-******************'); // Renders the PayPal button
</script>
Thanks
When you create the subscription, pass a unique custom_id
thanks Preston. This is defenitely the best option. I was having trouble because I was adding a new field with a random name to associate to the userID of the app. It turns out that you really need to use the custom_id key in order to receive it in the webhook calls.
Final solution was the following:
<script>
paypal.Buttons({
style: {
shape: 'pill',
color: 'gold',
layout: 'vertical',
label: 'subscribe'
},
createSubscription: function(data, actions) {
return actions.subscription.create({
/* Creates the subscription */
plan_id: '************************',
custom_id: 'appUserID'
});
},
onApprove: function(data, actions) {
alert(data.subscriptionID); // You can add optional success message for the subscriber here
}
}).render('#paypal-button-container-******************'); // Renders the PayPal button
</script>
Then the custom_id field will be available in all of the webhook calls. Perfect!

Paypal Smart Button Check Postal Code onApprove

The problem:
I am trying to figure out how to check the if postal code (entered during the paypal smart button check-out) meets specific requirements.
I have the createOrder, and onApprove object attributes I would like to check the postal code before i capture the payment so i can reject it, and cancel the order.
paypal.Buttons(
{
createOrder: function(data, actions) {
// This function sets up the details of the transaction, including the amount and line item details.
return actions.order.create({
purchase_units:
[
{
description: "Raffle Tickets",
custom_id: $('#cartID').val(),
soft_descriptor: "soft_descriptor",
amount: {
currency_code: "CAD",
value: cartVal,
breakdown: {
item_total: {
currency_code: "CAD",
value: cartVal
}
}
},
items: itemList
}
]
});
},
onApprove: function(data, actions) {
//id like to check the postal code here, or maybe do an ajax call then on success, call the
//capture function below to finalize the payment.
// This function captures the funds from the transaction.
return actions.order.capture().then(function(details) {
// This function shows a transaction success message to your buyer.
$('#details').val(JSON.stringify(details));
$('#frmConfirm').submit();
});
}
}).render('#paypal-button-container');
return actions.order.get().then(function(data,actions) {
console.log(data);
/* if logic on data.payer.address.postal_code goes here */
actions.order.capture().then(function(details) {
$('#details').val(JSON.stringify(details));
$('#frmConfirm').submit();
}
});
You mentioned AJAX; there are server-side equivalent APIs, if you want to switch from this client-side implementation to one that uses code on the server to validate. In that case the front-end would look more like this: https://developer.paypal.com/demo/checkout/#/pattern/server , and there is a v2/orders API call to get the details of the approved order before capturing it.