PayPal REST API - State doesn't change after purchase - paypal

So I have noticed that when checking on the status of a payment
GET /v1/payments/payment/<Payment-Id>
the "state" of a PayPal response does not change from "created" even after the PayPal user has purchased the item. It isn't until I execute the payment that the state changes to "approved".
This makes it difficult to tell the difference between a payment that has actually been approved by a client and one that is still in process as they are both in the "created" state. The only difference in the json response is that it seems to include shipping_address once the payment has actually been approved. I'm not sure that is a standard way to denote the difference though.
What is the standard way to tell if a client has actually approved a PayPal transaction when using the above call to the REST API?
Note: I already have the PHP callback scripts setup and working. I am working on a fail safe system that constantly checks database entries to make sure an approved payment was executed (in case of some system failure).
I thought I could use the IPN callback script I have setup from CLASSIC API but it seems that the REST API and CLASSIC API are not compatible since the IPN callback will not contain a transaction id or any necessary information to be useful.
It seems like when using the REST API, if you miss the redirect call to your webserver then that transaction is lost forever.

It is correct that you won't tell an approved payment from the state field in the lookup API response, instead, you would look for the payer object in the JSON body, and that piece of info will indicate an approved payment resource for you.
Here're the JSON responses of the same PAY-ID before/after the customer redirection (user approval)
Lookup payment /v1/payments/payment/PAY-9J02491382988403BK3BMC6I (before user approval):
{
"id": "PAY-9J02491382988403BK3BMC6I",
"intent": "sale",
"state": "created",
"cart": "07U14103P0008801U",
"transactions": [ {
"amount": {
"total": "80.00",
"currency": "USD"
},
"payee": {"email": "USM#email.com"},
"invoice_number": "55a460ff696br",
"item_list": {
"items": [
{
"name": "Test Ticket 1",
"sku": "55a460ff65f13",
"price": "10.00",
"currency": "USD",
"quantity": 1
},
{
"name": "Test Ticket 2",
"sku": "55a460ff66c7a",
"price": "20.00",
"currency": "USD",
"quantity": 2
},
{
"name": "Test Ticket 3",
"sku": "55a460ff66ce2",
"price": "10.00",
"currency": "USD",
"quantity": 3
}
],
"shipping_address": {
"recipient_name": "Test Name",
"line1": "Main St 1",
"city": "San Jose",
"state": "CA",
"postal_code": "95131",
"country_code": "US"
}
},
"related_resources": [],
"notify_url": "https://bt-pduan-1.c9.io/ipn.php"
}],
"redirect_urls": {
"return_url": "http://localhost:80/getpaypal?paymentId=PAY-9J02491382988403BK3BMC6I",
"cancel_url": "http://localhost:80/cancel"
},
"create_time": "2016-02-16T06:28:08Z",
"update_time": "2016-02-16T06:28:08Z",
"links": [
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-9J02491382988403BK3BMC6I",
"rel": "self",
"method": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v1/payments/payment/PAY-9J02491382988403BK3BMC6I/execute",
"rel": "execute",
"method": "POST"
},
{
"href": "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-07U14103P0008801U",
"rel": "approval_url",
"method": "REDIRECT"
}
]
}
Lookup the payment after user approval (I'm removing some JSON contents e.g. transaction/url arrays for readablity):
{
"id": "PAY-9J02491382988403BK3BMC6I",
"intent": "sale",
"state": "created",
"cart": "07U14103P0008801U",
"payer": {
"payment_method": "paypal",
"status": "VERIFIED",
"payer_info": {
"email": "USP#email.com",
"first_name": "Payer",
"last_name": "US",
"payer_id": "8FMFQ2KVYYHTY",
"shipping_address": {
"recipient_name": "Test Name",
"line1": "Main St 1",
"city": "San Jose",
"state": "CA",
"postal_code": "95131",
"country_code": "US"
},
"phone": "408-743-9795",
"country_code": "US",
"billing_address": {
"line1": "1 Main St",
"line2": "",
"city": "San Jose",
"state": "CA",
"postal_code": "95131",
"country_code": "US"
}
}
},
"transactions": [],
"redirect_urls": {},
"create_time": "2016-02-16T06:28:08Z",
"update_time": "2016-02-16T06:28:08Z",
"links": []
}
By checking the recorded PAY-ID looking for payer object in the API response, your will be able to save the orders and proceed the execute call in case it was missed in the customer return redirection.
Additionaly, neither IPN or webhooks would help in this case as they are async meesages triggered by transactional level events, which means there will be no notification until a payment is executed.

Related

Is Google Pay PayPal depreceated?

So my problem is, PayPal suggested a request type that is depreceated, and I don't know what API to use so my integration with Google Pay works with Paypal. This is the api PayPal suggested: Google Pay Integration . The API returns this:
`{
"name": "INTERNAL_SERVICE_ERROR",
"message": "An internal service error has occurred",
"information_link": "https://developer.paypal.com/docs/api/payments/#errors",
"debug_id": "41a6f4507a2df"
}`
body is this:
` {
"intent": "sale",
"payer": {
"payment_method": "paypal",
"funding_instruments": [
{
"credit_card_token": {
"credit_card_id": "CARD-1MD19612EW4364010KGFNJQI",
"external_customer_id": "joe_shopper408-334-8890"
}
}
],
"payer_info": {
"email": "sb-coy8y5423854#personal.example.com",
"payer_id": "AR6cGL6IXfI_BwzZGeBcpES7ilv1FZqJlYeMr3TZnp-07woQCczOi1oTjiiz4HQLqQgrMVPE6Ng2QafR"
}
},
"transactions": [
{
"amount": {
"total": "30.11",
"currency": "USD",
"details": {
"subtotal": "30.00",
"tax": "0.07",
"shipping": "0.03",
"handling_fee": "1.00",
"shipping_discount": "-1.00",
"insurance": "0.01"
}
},
"description": "The payment transaction description.",
"invoice_number": "48787589673",
"payment_options": {
"allowed_payment_method": "INSTANT_FUNDING_SOURCE"
},
"soft_descriptor": "ECHI5786786",
"item_list": {
"items": [
{
"name": "hat",
"description": "Brown hat.",
"quantity": "5",
"price": "3",
"tax": "0.01",
"sku": "1",
"currency": "USD"
},
{
"name": "handbag",
"description": "Black handbag.",
"quantity": "1",
"price": "15",
"tax": "0.02",
"sku": "product34",
"currency": "USD"
}
],
"shipping_address": {
"recipient_name": "Brian Robinson",
"line1": "4th Floor",
"line2": "Unit #34",
"city": "San Jose",
"country_code": "US",
"postal_code": "95131",
"phone": "011862212345678",
"state": "CA"
}
}
}
],
"note_to_payer": "Contact us for any questions on your order.",
"redirect_urls": {
"return_url": "https://yourwebsite.com/return",
"cancel_url": "https://yourwebsite.com/cancel"
}
} `
Any tips in what request I should use? I'm making the test with a credit card token because my country doesn't have google pay and paypal suport. But I need the PayPal API that accepts Google Pay direct token.
Edit:
I used this guide and it informs this API url: api-m.sandbox.paypal.com/v1/payments/payment
But it always returns the error described. The V2 request is totally different and don't have support (in the payments) for capture. Need a diferent paypal api request but I don't know what to use.

Is there a way to freeze an amount of money with PaypalAPI?

In general I have the flow described here: Is there a way to authorize orders calling the paypal api directly
In two words: just payments between users on my website with PaypalAPI
Now I am trying to figure out how to freeze an amount of money with PaypalAPI
An example of that would be when employers on certain platforms for freelancers place their project and confirm a freelancer. An amount of money is going to be frozen on their account. And, then in the end when the job is done, transferred to the freelancer.
This is possible. Paypal calls it 'Authorize and Capture'. It allows you to 'Authorize' a payment, which places a hold on the associated funds, and it can later be captured, i.e. paid out.
Here are the developer documents for the request. Here's a curl request from their docs that summarizes it pretty well.
curl -v https://api-m.sandbox.paypal.com/v1/payments/payment \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <Access-Token>" \
-d '{
"intent": "authorize",
"payer":
{
"payment_method": "paypal"
},
"transactions": [
{
"amount":
{
"total": "30.11",
"currency": "USD",
"details":
{
"subtotal": "30.00",
"tax": "0.07",
"shipping": "0.03",
"handling_fee": "1.00",
"shipping_discount": "-1.00",
"insurance": "0.01"
}
},
"description": "This is the payment transaction description.",
"custom": "EBAY_EMS_90048630024435",
"invoice_number": "48787589673",
"payment_options":
{
"allowed_payment_method": "INSTANT_FUNDING_SOURCE"
},
"soft_descriptor": "ECHI5786786",
"item_list":
{
"items": [
{
"name": "hat",
"description": "Brown color hat",
"quantity": "5",
"price": "3",
"tax": "0.01",
"sku": "1",
"currency": "USD"
},
{
"name": "handbag",
"description": "Black color hand bag",
"quantity": "1",
"price": "15",
"tax": "0.02",
"sku": "product34",
"currency": "USD"
}],
"shipping_address":
{
"recipient_name": "Hello World",
"line1": "4thFloor",
"line2": "unit#34",
"city": "SAn Jose",
"country_code": "US",
"postal_code": "95131",
"phone": "011862212345678",
"state": "CA"
}
}
}],
"note_to_payer": "Contact us for any questions on your order.",
"redirect_urls":
{
"return_url": "https://example.com/return",
"cancel_url": "https://example.com/cancel"
}
}'

Paypal API v2-API response missing seller_receivable_breakdown

How could I find the seller_receivable_breakdown field from Paypal v2 API?
When I capture the payment, I don't get a seller_receivable_breakdown back in the API response, which should contain the net_amount.
Is there a reason why?
API Response:
{
"create_time": "2020-05-08T18:06:08Z",
"id": "35W12417YE077383Y",
"intent": "CAPTURE",
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/35W12417YE077383Y",
"method": "GET",
"rel": "self",
"title": "GET"
}
],
"payer": {
"address": {
"country_code": "SG"
},
"email_address": "sb-vnmn01690912#personal.example.com",
"name": {
"given_name": "John",
"surname": "Doe"
},
"payer_id": "ZMVMLDB2Q3RNS"
},
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "76.00"
},
"payee": {
"email_address": "sb-dtlz4548886#business.example.com",
"merchant_id": "9D3W7QWY9MULE"
},
"payments": {
"captures": [
{
"amount": {
"currency_code": "USD",
"value": "76.00"
},
"create_time": "2020-05-08T18:06:37Z",
"final_capture": true,
"id": "1DU784512L090023U",
"links": [
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/1DU784512L090023U",
"method": "GET",
"rel": "self",
"title": "GET"
},
{
"href": "https://api.sandbox.paypal.com/v2/payments/captures/1DU784512L090023U/refund",
"method": "POST",
"rel": "refund",
"title": "POST"
},
{
"href": "https://api.sandbox.paypal.com/v2/checkout/orders/35W12417YE077383Y",
"method": "GET",
"rel": "up",
"title": "GET"
}
],
"seller_protection": {
"dispute_categories": [
"ITEM_NOT_RECEIVED",
"UNAUTHORIZED_TRANSACTION"
],
"status": "ELIGIBLE"
},
"status": "COMPLETED",
"update_time": "2020-05-08T18:06:37Z"
}
]
},
"reference_id": "default",
"shipping": {
"address": {
"address_line_1": "123 Thomson Rd.",
"admin_area_1": "SG_zip = 308123",
"admin_area_2": "Singapore",
"country_code": "SG",
"postal_code": "308123"
},
"name": {
"full_name": "Doe John"
}
}
}
],
"status": "COMPLETED",
"update_time": "2020-05-08T18:06:37Z"
}
Either it may not be there in sandbox mode, or you may need to do a get operation on the capture id.
https://developer.paypal.com/docs/api/payments/v2/#captures_get
I contacted PayPal support about this and this is their response:
Unfortunately, it looks like the capture response will not include the "seller_receivable_breakdown" if the capture is still pending. You can see that mentioned under the "seller_receivable_breakdown" variable in this documentation about the captures object. If the capture is pending or processing still, (this happens often when a customer opts for a card transaction rather than a PayPal account balance transaction), then the capture response won't show the seller_receivable_breakdown but you can call for details on the capture right after it has finished to then see that object.
PayPal may hold a "suspicious" transaction for few days and the transaction is in a "Pending" state during the hold. Some transaction might even require your personal approval. While the transaction is on hold you cannot get the seller_receivable_breakdown via API (though this data is available in the dashboard).
You can setup a WebHook to fire on "Payment capture completed" and retrieve the information there. Be ready that the WebHook might fire in couple of days after the order.

Paypal's transaction object with failing transactions

We are using Paypal Pro to do direct credit card processing. What happens when I put 2 transactions in a request, and the credit card approves one transaction object, but declines the other because of lack of funds. Does PayPal throw away the entire transaction and returns an error?
Take the following code straight from paypals rest api for node.js
var payment_details = {
"intent": "sale",
"payer": {
"payment_method": "credit_card",
"funding_instruments": [{
"credit_card": {
"type": "visa",
"number": "4417119669820331",
"expire_month": "11",
"expire_year": "2018",
"cvv2": "874",
"first_name": "Joe",
"last_name": "Shopper",
"billing_address": {
"line1": "52 N Main ST",
"city": "Johnstown",
"state": "OH",
"postal_code": "43210",
"country_code": "US" }}}]},
"transactions": [{
"amount": {
"total": "7.47",
"currency": "USD",
"details": {
"subtotal": "7.41",
"tax": "0.03",
"shipping": "0.03"}},
"description": "This is the payment transaction description." }]};
paypal_sdk.payment.create(payment_details, function(error, payment){
if(error){
console.error(error);
} else {
console.log(payment);
}
});
What happens when we put 2 transaction objects in there, will we have to handle the case of the credit card declining on the second transaction?
This is what we ended up using. We used the items feild to tell paypal about the multiple items in the transaction. Items is an array of items:
items.push({
quantity: "1",
name: classList[i].name,
price: price.toFixed(2),
currency: "USD"
});
var create_payment_json = {
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://"+config.host+"/payment/success/"+paymentId,
"cancel_url": "http://"+config.host+"/payment/cancel/"+paymentId
},
"transactions": [{
"item_list": {
"items": items
},
"amount": {
"currency": "USD",
"total": (total.toFixed(2))
},
"description": description
}]
};

PayPal REST API does not take the customized shipping address

I am following the PayPal REST API Reference at https://developer.paypal.com/webapps/developer/docs/api/ to create and execute payments.
To create a payment, I send the following data to PayPal. The data contain "shipping_address". The payment is created successfully.
{
"intent": "sale",
"payer": {
"payment_method": "paypal"
},
"redirect_urls": {
"return_url": "http://www.somethingabc.com/approve",
"cancel_url": "http://www.somethingabc.com/cancel"
},
"transactions": [{
"amount": {
"currency": "USD",
"total": "10.00",
"details": {
"shipping": "0.00",
"subtotal": "10.00",
"tax": "0.00"
}
},
"item_list": {
"items": [{
"quantity": "1",
"name": "Apples",
"price": "10.00",
"currency": "USD"
}],
"shipping_address": {
"recipient_name": "John",
"type": "residential",
"line1": "441 Waterdale RD",
"city": "Heidelberg West",
"country_code": "AU",
"postal_code": "3081",
"state": "VICTORIA"
}
}
}]
}
Then, I redirect the web browser to the approval_url (e.g., https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=EC-343434SADSDSAD34) given in the PayPal response for the user to login and approve the payment. The user logins and a PayPal review web page appears. I expect that this review page should show the customized shipping address which I have provided previously when the payment was created. However, the PayPal review web page shows the PayPal owner's address instead.
So, my question is how to force the PayPal review web page to show the customized shipping address instead of the PayPal owner's address? If this can't be done, how can I get the shipping address which the user has selected on the PayPal review web page (when I call the API to execute the payment, the selected shipping address is not included in the payer_info object!).
Thanks.
After testing your code I cannot find any errors. The response I get when completing the payment:
{
"id": "PAY-xxxxxxx",
"create_time": "2014-05-01T23:54:00Z",
"update_time": "2014-05-01T23:59:35Z",
"state": "approved",
"intent": "sale",
"payer": {
"payment_method": "paypal",
"payer_info": {
"email": "test#paypal.com",
"first_name": "John",
"last_name": "Smith",
"payer_id": "GPV2878GCMNGE",
"shipping_address": {
"line1": "441 Waterdale RD",
"city": "Heidelberg West",
"state": "Victoria",
"postal_code": "3081",
"country_code": "AU"
}
}
},
"transactions": [
{
"amount": {
"total": "10.00",
"currency": "USD",
"details": {
"subtotal": "10.00"
}
},
"item_list": {
"items": [
{
"name": "Apples",
"price": "10.00",
"currency": "USD",
"quantity": "1"
}
],
"shipping_address": {
"recipient_name": "John",
"line1": "441 Waterdale RD",
"city": "Heidelberg West",
"state": "VICTORIA",
"postal_code": "3081",
"country_code": "AU"
}
},
.....
}
If there you are not seeing this I would reach out to Paypal's Technical Support and file a trouble ticket. As this is the response you should get.
You will need to create an Experience Profile with the address_override option set to 1. The customer will then be unable to change the shipping address you have passed to PayPal.
You would include the experience_profile_id in your JSON request when initiating the sale.