Is this really how you do a one-time payment? - paypal

I've completed the monthly subscription payment which looks something like this in a simple way...
paypal.Buttons({
createSubscription: function(data, actions) {
return actions.subscription.create({
'plan_id': 'P-2UF78835G6983425GLSM44MA'
});
},
onApprove: function(data, actions) {
alert('You have successfully created subscription ' + data.subscriptionID);
}
}).render('#paypal-button-container');
As you can see above you make a plan first through postman and pass in the plan_id. With the plans you can patch and what not.
Now since I'm onto a one-time payment this is the way I guess you're supposed to do it?
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: [{
amount: {
value: '0.01'
}
}]
});
}
}).render('#paypal-button-container');
Is there an order_id or something I can pass in, because you can use postman to create orders correct? So you can patch the amount or whatever if you want.
The paypal docs are a little bit all over the place and it's not very clear. I'm using the smart buttons, not the SDK.

Your code shows the client-side version of creating an order. You can find a full demo pattern of the same here: https://developer.paypal.com/demo/checkout/#/pattern/client
If you want to create an order_id on your server, then you would instead use the server-side demo pattern to fetch that order_id: https://developer.paypal.com/demo/checkout/#/pattern/server
The server-side pattern is better and more secure, if you're capable of implementing it. The only caveat I would add is, once you get everything working for the happy path, don't neglect to handle funding source failures -- so that if the capture fails on the server end when e.g. a payer's first card is declined, this is propagated back to the UI and they can just select a different one.

Related

PayPal return URL with transaction or payment ID?

I'm using the following script to display a PP button on my website which works fine. You'll see that I have a return URL which also pulls in data (eg orderid) from my page:
<script>
paypal.Buttons({
// Sets up the transaction when a payment button is clicked
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '2.00' // Can reference variables or functions. Example: `value: document.getElementById('...').value`
}
}]
});
},
// Finalize the transaction after payer approval
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Successful capture! For dev/demo purposes:
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
var transaction = orderData.purchase_units[0].payments.captures[0];
window.location.href = 'https://mywebsite.co.uk/signup-payment-confirmation.asp?fee=<%=fee%>&ct=<%=orderid%>';;
});
}
}).render('#paypal-button-container');
</script>
This works fine as when a transaction is made the user is simply redirected to the return URL and I record the data in my database. However, what I really need is for my return URL to also show the transaction ID from the PayPal transaction. I can then tally records in my database to those in the PayPal admin area.
actions.order.create / .capture are for simple use cases. If you intend to do anything with a database, do not use these client side functions. Any number of problems could prevent your system from recording a transaction after the fact.
Instead, use the actual v2/checkout/orders API and make two routes (url paths) on your server, one for 'Create Order' and one for 'Capture Order'. You could use one of the (recently deprecated) Checkout-*-SDKs for the routes' API calls to PayPal, or your own HTTPS implementation of first getting an access token and then doing the call. Both of these routes should return/output only JSON data (no HTML or text). Inside the 2nd route, when the capture API is successful you should verify the amount was correct and 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 reserving product or sending an email) immediately before forwarding return JSON to the frontend caller. In the event of an error forward the JSON details of it as well, since the frontend must handle such cases.
Pair those 2 routes with this frontend approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server . (If you need to send any additional data from the client to the server, such as an items array or selected options, add a body parameter to the fetch with a value that is a JSON string or object)

Paypal shop link sequrity

For receiving money from clients here
https://www.paypal.com/buttons/smart
can be copy a code where is an onApprove function
onApprove: function(data, actions) {
return actions.order.capture().then(function(orderData) {
// Full available details
console.log('Capture result', orderData, JSON.stringify(orderData, null, 2));
// Show a success message within this page, e.g.
//const element = document.getElementById('paypal-button-container');
//element.innerHTML = '';
//element.innerHTML = '<h3>{{__("Thank you for your payment!")}}</h3>';
// Or go to another URL: actions.redirect('http://www.myservices.com/oneserviceBought?id=2323');//for example
});
How to rise security, that somebody (cyber attacker) cannot steal the service - instead of paying throw PP, copy the url http://www.myservices.com/oneserviceBought?id=2323 on his/her browser?
The only way to secure such information is for the transaction to be created and captured from the server, and for only the server to eventually return or provide the redirect (or other credentials) once a capture is verified as successful.
There are details within Set up standard payments that explain how to program a server integration. Read that information, including the code demo pattern it links to.

How to fail a transaction in payment.create in paypal Express CheckOut?

How to fail a transaction using the below code for paypal?
payment: function(data, actions) {
return actions.payment.create({
transactions: [{
amount: {
total: '0.04',
currency: 'USD'
}
}]
});
},
// Execute the payment
onAuthorize: function(data, actions) {
return actions.payment.execute(
).then(function(result) {
})
}
This jQuery written in client side. I get result.ack as success. How to get result.ack as Failure. So what request I have to pass in payment.create ()
You appear to be using an old version of the PayPal Checkout, called 'checkout.js'. You should instead upgrade to the latest JS SDK; here is a demo: https://developer.paypal.com/demo/checkout/#/pattern/client
But regardless, there is no way to make the create call fail on a client-side only integration other than simply passing a bad value to it.
Negative testing for REST APIs is available for server-based integration patterns (documented here). For the case of a JS create action, the server would then fail to return a valid id string. This results in a JS console error and halts further execution. The onError function would also be triggered, if one is defined. Effectively this is the same as what happens in the above mentioned case (passing a bad value to the create function)

PayPal transaction with a mix of one time and recurring payments using Smart Payment Buttons

My project runs on products with both the one time and recurring payments.
Right now I'm trying to integrate the paypal smart buttons, which would create one transaction consisting of one time and recurring payments.
For example: The user adds to the cart Product1 (5$ one time payment) and Product2 (15$ recurring payment), which makes it a 20$ transaction.
Is it possible to charge the user 20$ in the beginning and set a trial on the recurring product (Product2 in our case), which would charge the user from the next billing period?
Here's what I tried:
On the js side I have the following code
createPayPalButton = function() {
// setting up the details for the transaction
createOrder: function (data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: '20.00'
}
}]
})
},
// setting up the details for subscription
createSubscription: function (data, actions) {
return actions.subscription.create({
'plan_id': 'MY_PLAN_ID'
});
},
}
$(function() {
createPayPalButton();
})
But after running it I receive the following error:
Uncaught Error: Do not pass both createSubscription and createOrder
I tried to find a solution on the paypal side, but I didn't manage to.
Any guidance would be much appreciated. Thank you.
You could create a new bespoke plan with a setup_fee that corresponds to the amount of the one-time order.
Otherwise you need a separate checkout and transaction.

Where to enter paypal IPN url and how to pass custom data?

This seems a very naive set of questions, but I really couldn't find it.
Question 1) I am trying to implement IPN for PayPal. I want to enter The listener URL, but I couldn't find where. I've searched the internet and all the responses are old. Where could I put it?
Question 2) I am using the following code and I want to pass custom data so that when IPN sends me back the transaction message, It will have that custom data in it. I need to pass 2 custom data. How is this possible for the following code? I really couldn't find this one in the docs too.
<template>
<div id="paypal-button-container"></div>
</template>
<script>
export default {
props: ["amount"],
mounted() {
let self = this;
paypal
.Buttons({
createOrder: function(data, actions) {
// Set up the transaction
return actions.order.create({
purchase_units: [
{
amount: {
value: self.amount,
}
}
],
});
},
onApprove: function(data, actions) {
// Capture the funds from the transaction
return actions.order.capture().then(function(details) {
// Show a success message to your buyer
console.log(details);
self.$emit("paypalPaySuccess", {
order_id: details.id,
})
alert("Transaction completed by " + details.payer.name.given_name);
}).catch((err)=>{
})
}
})
.render("#paypal-button-container");
}
};
</script>
Addition of some questions
I am using all the events in dashboard.
I need to add some more questions, because I can't find the information I need.
Question 3) In client-side, I make action.orders.capture().then . which means that I capture funds immediatelly. When webhooks come to my back-end, there're 2 webhooks that come for single payment . (PAYMENT.CAPTURE.PENDING and CHECKOUT.ORDER.APPROVED) . a) why doesn't PAYMENT.CAPTURE.COMPLETED arrive as webhook event? b) what if I want to get DENIED events? If PAYMENT.CAPTURE.COMPLETED doesn't arrive, PAYMENT.CAPTURE.DENIED won't arrive too.
Question 4) As I said, there're 2 webhook events appearing for single payment on my back-end. The first one has id in it. Let's say I store this in my database as id and status(PENDING). Then another event came which is CHECKOUT.ORDER.APPROVED. Now, this one has different id then the previous one. So I can't really go back to database and update the status as this has different id. What should I do? One thing I noted is that the second came event has also another id (the previous came event) somewhere in captured object. is this what i should use it? which one is the final transaction_id?
Question 5) Turns out invoice_id should be unique each time. So I wanted to pass user_id but i guess i also should generate random string and append it to user_id something like this: invoice_id: '7,randomstring'. right?
Question 6) Same event sometimes comes twice. Why is that? Should I return status 200 in order for that same event not to come again? When will it be good to return that status? I guess after I update the database, right?
Q1: IPN is deprecated. You should be using webhooks, such as CHECKOUT.ORDER.APPROVED
Q2: There does not appear to be any specific way to pass custom data.
Option 1:
What I ended up doing was using the invoice_id field in purchase_units. I tested and found that I could pass a reasonably long string of characters and they did not have to be unique. It seems a bit of a hack but it works.
When you implement the CHECKOUT.ORDER.APPROVED webhook, you can then parse the data in purchase_unit invoice_id.
Option 2:
Another idea I considered which doesn't need to hack the invoice_id, is to send data to the server using the details from the capture event in the javascript, store that custom information, along with the order id in the database, and then when the webhook occurs, cross-match the order id to the saved one to retrieve the information.
I prefer option 1, but your needs may vary.
It's pretty indicative though of how poorly designed the whole PayPal API is. There are huge gaps in the implementation, very little documentation, and zero support.
I had to implement Stripe as well. The entire implementation took me 3 days. The same PayPal implementation took me over 3 weeks! The lack of documentation, having to constantly just try things out, and the inconsistency of the sandbox implementation were real time killers.