How to test failed transactions with the PayPal JS SDK - paypal

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.

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.

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 :)

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

Braintree Google Pay sandbox opens live domain

I'm trying to integrate Google Pay at my page, as expected doing that first at Sandbox environment but I face a problem that when I click the Google Pay button it opens the live domain and asks me to enter a real card, although I setup up all related to Sandbox environment.
Here is the code following BT documentation.
var createGooglePaymentComponent = function(clientInstance){
var button = document.querySelector('#google-pay-button');
var paymentsClient = new google.payments.api.PaymentsClient({
environment: 'TEST' // Or 'PRODUCTION'
});
braintree.googlePayment.create({
client: clientInstance,
googlePayVersion: 2,
}, function (googlePaymentErr, googlePaymentInstance) {
paymentsClient.isReadyToPay({
apiVersion: 2,
apiVersionMinor: 0,
allowedPaymentMethods: googlePaymentInstance.createPaymentDataRequest().allowedPaymentMethods,
}).then(function(response) {
if (response.result) {
button.addEventListener('click', function (event) {
event.preventDefault();
var paymentDataRequest = googlePaymentInstance.createPaymentDataRequest({
transactionInfo: {
currencyCode: 'USD',
totalPriceStatus: 'FINAL',
totalPrice: '100.00',
}
});
var cardPaymentMethod = paymentDataRequest.allowedPaymentMethods[0];
cardPaymentMethod.parameters.billingAddressRequired = true;
paymentsClient.loadPaymentData(paymentDataRequest).then(function(paymentData) {
googlePaymentInstance.parseResponse(paymentData, function (err, result) {
if (err) {
// Handle parsing error
}
// Send result.nonce to your server
});
}).catch(function (err) {
});
});
}
}).catch(function (err) {
});
});
};
Here is a screenshot of what I get:
Any idea why does that happen?
Using Google Pay in the TEST environment will return a TEST payment credential which won't actually charge the payment method that you provide. It's understandable that you don't want to be using real payment details.
If you want to be able to choose from a list of predefined test cards, follow the instructions here: https://developers.google.com/pay/api/web/guides/resources/test-card-suite
In short, you will need to join the googlepay-test-mode-stub-data Google Group which will then display a list of test accounts when accessing the Google Pay payment sheet with that user.

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.