Paypal Smart Button Check Postal Code onApprove - paypal

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.

Related

How to test failed transactions with the PayPal JS SDK

We are using the PayPal JS SDK ^5.1.0 and I'm using it to generate a pay button like described in the docs:
const paypal = await loadScript({
"client-id": conf.client_id,
"currency": conf.currency_code
});
await paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: (coData, actions) => {
if (price.getTotal()) {
data.data.amount = price.getTotal();
return actions.order.create({
purchase_units: [{
description: config.registrationCenterDisplayName,
amount: {
value: price.getTotal().toFixed(2) // Can also reference a variable or function
},
}],
application_context: {
shipping_preference: 'NO_SHIPPING'
}
});
} else {
throw new Error('Amount can not be 0');
}
},
// Finalize the transaction after payer approval
onApprove: (oaData, actions) => {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
const transaction = orderData.purchase_units[0].payments.captures[0];
data.data.id = transaction.id;
data.data.status = transaction.status;
dataValid = true;
submit();
});
}
}).render(buttonWrapper[0]);
It seems not to work well with error case i live. How can I provoke failed transactions or capture erros in sandbox mode?
I found the negative testing mode of a sandbox, but it does not change the behaviour for the button. I still get only positive responses.
I opened also an issue in the github repo: https://github.com/paypal/paypal-js/issues/273
Thx a lot for any help! I might overlook something very obvious...
For client-side captures, actions.order.capture().then( is only triggered when a capture is successful.
Failure cases are handled by the JS SDK itself, there is no need to test anything as it is not your code.
For server-side capturing (not your question) and using the JS SDK for approval, see the demo code here. Negative testing can be used from server API calls for failures if desired.

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');

PayPal Subscription Button Coupons

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 */
}
}
}

Paypal Smart Button Subscription Status

I am new to set up Subscription using Paypal Smart buttons. I have created a Paypal smart button subscribe from Paypal account and have pasted it in my html. Have added my OnInit and Onclick function to validate before user clicks.
I am checking if the subscription is Active or not after the user goes through the "agree and subscribe" page. (This is done in "notify.php" where I call using paypal REST api and get the subscription status)
<div id="paypal-button-container"></div>
<script src="https://www.paypal.com/sdk/js?client-id=XXXXXXXXXXXXXXXX&vault=true&intent=subscription" data-sdk-integration-source="button-factory"></script>
<script>
paypal.Buttons({
style: {
shape: 'pill',
color: 'gold',
layout: 'vertical',
label: 'subscribe'
},
// onInit is called when the button first renders
onInit: function(data, actions) {
// Disable the buttons
actions.disable();
// Listen for changes to the radio
var items = document.getElementsByName("settings");
[].forEach.call(items, function (item) {
item.addEventListener("change", function () {
// Enable or disable the button when it is checked or unchecked
if (event.target.checked) {
actions.enable();
} else {
actions.disable();
}
});
});
},
// onClick is called when the button is clicked
onClick: function() {
var radios=document.querySelectorAll('input[type="radio"]:checked').length;
// Show a validation error if the checkbox is not checked
if (radios == 0) {
document.querySelector('#error').classList.remove('hidden');
}
},
createSubscription: function(data, actions) {
return actions.subscription.create({
'plan_id': 'P-XXXXXXXXXXXX'
});
},
onApprove: function(data, actions) {
alert(data.subscriptionID);
alert(data.SubscriptionDate);
// You must return a promise from onClick to do async validation
return fetch("notify.php", {
method: 'post',
headers: {
'content-type': 'application/json'
},
body:JSON.stringify({
subscriptionID:data.subscriptionID
})
}).then(response => {
return response.text();
})
.then(data => {
console.log(data);
if(data == "ACTIVE"){
location.href = "validate.html?data=ACTIVE";
}
})
.catch(error => {console.log(error)
});
}
}).render('#paypal-button-container');
</script>
</div>
Q1. Could somebody please confirm when the subscription status returns as "ACTIVE", it means that the user has paid and has subscribed successfully. And thus I can update this information in my DB.
OR Do I need to capture the transaction in OnApprove as below:
onApprove: function (data, actions) {
// Capture the transaction funds
return actions.order.capture().then(function () {
Q2. Is this way of integrating the button on my web page is secure?
UPDATE:
(All this is for my personal webpage where I am providing a service to customers. I want to activate a subscription for them.)
I made a subscription plan from my Paypal account. Added the generated code on my webpage and now I want to check whether the user/customer has paid/subscribed successfully to my service so that I can enable service for them and also update their information in my DB.
An active PayPal subscription does not indicate the first payment has already been made.
Subscriptions bill on a batch schedule. To be notified when a successful payment is made, subscribe to the Webhook event PAYMENT.SALE.COMPLETED