PayPal Smart buttons patch order via PHP API - php

I'm implementing Smart Buttons with Express checkout, so customers can select the delivery address on Paypal's popup. As agreed with Paypal support, I'm doing the communication with Paypal servers via PHP instead of Javascript. So in the onShippingChange event, I'm calling my server to calculate the delivery price and patch the order so Paypal updates the delivery price. The success path works fine, I need your help on error case(s).
How the patch request shall look like to instrument Paypal to display the warning that my webshop is not delivering to the selected location? (When implementing on Javascript, this was the return actions.reject in the onShippingChange.)
Thanks!
This is the patch request of a successful patch:
{
"path": "/v2/checkout/orders/xxxxxxxxxxxxxxxxxxxxx?",
"body": [
{
"op": "replace",
"path": "/intent",
"value": "CAPTURE"
},
{
"op": "replace",
"path": "/purchase_units/#reference_id=='default'/amount",
"value": {
"currency_code": "GBP",
"value": 265.95,
"breakdown": {
"item_total": {
"currency_code": "GBP",
"value": 236
},
"shipping": {
"currency_code": "GBP",
"value": 29.95
}
}
}
}
],
"verb": "PATCH",
"headers": {
"Content-Type": "application/json"
}
}

in the onShippingChange event, I'm calling my server to calculate the delivery price and patch the order
Great. But if the address is unsupported, your server must return the failed status in its response to that call. Based on that response, onShippingChange must return actions.reject() to the calling PayPal JS.

Related

Issue while trying to implement Paypal Create Order API

About the issue
This is about create order api in Paypal. Documentation link is here I am trying to pass below payload, so that the request could have my return and cancel url and everything works perfectly.
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}
],
"application_context" => [
"return_url" => "my return url",
"cancel_url" => "my cancel url"
]
Just the return and cancel url has gone deprerated in application_context.
To overcome this problem, I removed application_context from payload and added payment_source like below which has return and cancel url
"intent": "CAPTURE",
"purchase_units": [
{
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}
],
"payment_source": {
"paypal": {
"experience_context": {
"return_url": "return Url",
"cancel_url": "cancel Url"
}
}
}
Now, it gives an error message - PAYPAL_REQUEST_ID_REQUIRED
I need to pass return and cancel url and at this stage I only need to create the request to let user go to checkout page. that's it. I really don't have any payment info yet.
To resolve the error message "PAYPAL_REQUEST_ID_REQUIRED", you need to including a unique identifier in the "request_id" field in the payload.
something like:
[
"intent":"CAPTURE",
"purchase_units":[
{
"amount":{
"currency_code":"USD",
"value":"100.00"
}
}
],
"payment_source":{
"paypal":{
"experience_context":{
"return_url":"return Url",
"cancel_url":"cancel Url"
}
}
},
"request_id":"UNIQUE_REQUEST_ID"
]
You may also verify that the return and cancel URLs provided in the payload are valid and accessible.
Inside your payment_source add "request_id": "YOUR_UNIQUE_REQUEST_ID"
Your system can generate this request Id yourself and must be unique to prevent getting error message DUPLICATED_REQUEST_ID
See here in documentation https://developer.paypal.com/api/rest/requests
PayPal-Request-Id
"Optional. Contains a unique user-generated ID that the server stores for a period of time. Use this header to enforce idempotency on REST API POST calls. ...."
You need request_id could you try including reference_id.
{
"intent": "CAPTURE",
"purchase_units": [
{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}
],
"payment_source": {
"paypal": {
"experience_context": {
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl"
}
}
}
}'
https://developer.paypal.com/api/rest/reference/orders/v2/errors/
PAYPAL_REQUEST_ID_REQUIRED: A PayPal-Request-Id is required if you are trying to process payment for an Order. Please specify a PayPal-Request-Id or Create the Order without a payment_source specified.
Add this param in your headers
PayPal-Request-Id
string [ 1 .. 36 ] characters
The server stores keys for 6 hours. The API callers can request the times to up to 72 hours by speaking to their Account Manager.

Paypal REST API - Payment capture response missing exchange rate

We have a Sandbox store account using Euro currency and configured to convert payments in the store currency.
If I make a purchase in US dollars, I would expect at least the exchange rate and the converted amount in the API capture response, but they are missing.
I'm calling the following API using PHP Curl with a fresh access token:
https://api-m.sandbox.paypal.com/v2/payments/captures/
and I get this response:
{
"id":"63F99991HX835842N",
"amount":{
"currency_code":"USD",
"value":"600.00"
},
"final_capture":true,
"seller_protection":{
"status":"ELIGIBLE",
"dispute_categories":[
"ITEM_NOT_RECEIVED",
"UNAUTHORIZED_TRANSACTION"
]
},
"disbursement_mode":"INSTANT",
"seller_receivable_breakdown":{
"gross_amount":{
"currency_code":"USD",
"value":"600.00"
},
"paypal_fee":{
"currency_code":"USD",
"value":"23.70"
},
"net_amount":{
"currency_code":"USD",
"value":"576.30"
}
},
"invoice_id":"10000000006",
"status":"COMPLETED",
"supplementary_data":{
"related_ids":{
"order_id":"EC-9F92771459179154X",
"authorization_id":"8T758094XY4322813"
}
},
"create_time":"2022-12-12T11:29:38Z",
"update_time":"2022-12-12T11:29:38Z",
"links":[
{
"href":"https://api.sandbox.paypal.com/v2/payments/captures/63F99991HX835842N",
"rel":"self",
"method":"GET"
},
{
"href":"https://api.sandbox.paypal.com/v2/payments/captures/63F99991HX835842N/refund",
"rel":"refund",
"method":"POST"
},
{
"href":"https://api.sandbox.paypal.com/v2/payments/authorizations/8T758094XY4322813",
"rel":"up",
"method":"GET"
}
]
}
As stated in the Paypal API reference, I should see the following which instead are missing:
"seller_receivable_breakdown": {
"gross_amount": {
"total": "10.99",
"currency": "USD"
},
"paypal_fee": {
"value": "0.33",
"currency": "USD"
},
"net_amount": {
"value": "10.66",
"currency": "USD"
},
"receivable_amount": {
"currency_code": "CNY",
"value": "59.26"
},
"paypal_fee_in_receivable_currency": {
"currency_code": "CNY",
"value": "1.13"
},
"exchange_rate": {
"source_currency": "USD",
"target_currency": "CNY",
"value": "5.9483297432325"
}
},
What am I doing wrong?
Thanks!
We tried to purchase in US Dollars on a Sandbox store with Euro currency, and we expect the currency conversion and exchange rate in the Capture API response, which are missing.
Ok I found the problem, we had configured the Sandbox store to accept USD and GBP currencies too, that's why Paypal did not do the conversion:
Money, Banks and Cards -> Currency Management menu must accept only the main currency

PHP Paypal MALFORMED_REQUEST_JSON although JSON seems to be well formatted

I try to implement a paypal express checkout flow to a website.
The user must be able to adjust the order after they have logged in with paypal and choosed their payment option.
To do so, I create the order with paypal with the intent "AUTHORIZE" and user action "CONTINUE". It is created, the user is sent back to our website and I can fetch order and payer information with the (order) id created.
But then, when the payer is done checking and adjusting some final parameters and clicks the "buy now" button, I want to send the update order call as referenced here:
https://developer.paypal.com/api/orders/v2/#orders_patch
I create the payload in exactly the same way as for order creation, but this time with intent "CAPTURE" and user_action="PAY_NOW". I PATCH it to the correct path, but I keep getting the failure message "MALFORMED_REQUEST_JSON" .
Hoewever, the JSON itself is a valid JSON, no errors thrown in creation. Here is a sample:
{
"intent": "CAPTURE",
"application_context": {
"landing_page": "NO_PREFERENCE",
"shipping_preference": "SET_PROVIDED_ADDRESS",
"user_action": "PAY_NOW"
},
"purchase_units": [{
"reference_id": "2289256",
"description": "Your order at site",
"custom_id": "order id 2289256",
"soft_descriptor": "site name",
"invoice_id": "2289256",
"amount": {
"currency_code": "EUR",
"value": 59.98,
"breakdown": {
"item_total": {
"currency_code": "EUR",
"value": 50.41
},
"shipping": {
"currency_code": "EUR",
"value": 0
},
"discount": {
"currency_code": "EUR",
"value": 0
},
"tax_total": {
"currency_code": "EUR",
"value": 9.57
}
}
},
"items": [{
"name": "Product 1",
"description": "Product 1 Description",
"sku": "1019879",
"unit_amount": {
"currency_code": "EUR",
"value": 16.8
},
"tax": {
"currency_code": "EUR",
"value": 3.19
},
"quantity": "1",
"category": "PHYSICAL_GOODS"
}, {
"name": "Product 2",
"description": "Product 2 Description",
"sku": "1024593",
"unit_amount": {
"currency_code": "EUR",
"value": 33.61
},
"tax": {
"currency_code": "EUR",
"value": 6.38
},
"quantity": "1",
"category": "PHYSICAL_GOODS"
}],
"shipping": {
"name": {
"full_name": "John Doe"
},
"address": {
"address_line_1": "Badensche Str. 24",
"address_line_2": "",
"admin_area_2": "Berlin(Berlin)",
"postal_code": "10715",
"country_code": "DE"
}
}
}]
}
Here is paypal´s response
{
name: "INVALID_REQUEST",
message: "Request is not well-formed, syntactically incorrect, or violates schema.",
debug_id: "c315ce9eb90b4",
details: [{
field: "/",
location: "body",
issue: "MALFORMED_REQUEST_JSON",
description: "The request JSON is not well formed.",
}],
links: [{
href: "https://developer.paypal.com/docs/api/orders/v2/#error-MALFORMED_REQUEST_JSON",
rel: "information_link",
encType: "application/json",
}],
}
I just cannot figure out the problem. I tried to remove the whole application_context, purchase_units, intent and see if there is a problem within any of these parameters. Nothing changed. What exactly is wrong with this call?
That's not how a patch operation works. A patch operation's JSON payload must look something like the example in the API reference:
'[
{
"op": "replace",
"path": "/purchase_units/#reference_id==\'default\'/shipping/address",
"value": {
"address_line_1": "123 Townsend St",
"address_line_2": "Floor 6",
"admin_area_2": "San Francisco",
"admin_area_1": "CA",
"postal_code": "94107",
"country_code": "US"
}
}
]'
However, based on the information provided it's unclear why you are attempting to use a PATCH, since you do not list any relevant fields for a patch.
Intent authorize and intent capture are for creating an order. Use one or the other, never both for the same transaction. To capture an order that was created with intent capture, use a capture API call. To authorize an order that was created with intent authorize, use an authorize API call. The relevant API endpoints for either will be in the API response when you create the order, or you can read the documentation.
Since it appears your intended result is a captured payment (rather than an authorization), intent authorize is not relevant to your use case.

How do I post PayPal array response details to database using Laravel

How do I post this Paypal response details to database
I have succefully made payment using smart button paypal.
I am able to capture the response details using console log.
Now How do I post the response array to database using laravel.
Onapproval
onApprove: function(data, actions) {
let token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
// This function captures the funds from the transaction.
return actions.order.capture().then(function(details) {
if(details.status == 'COMPLETED'){
return fetch('/pages/save', {
method: 'post',
headers: {
'content-type': 'application/json',
"Accept": "application/json, text-plain, */*",
"X-Requested-With": "XMLHttpRequest",
"X-CSRF-TOKEN": token
},
body: JSON.stringify({
orderId : data.orderID,
id : details.id,
status: details.status,
payerEmail: details.payer.email_address,
})
})
.then(status)
.then(function(response){
// redirect to the completed page if paid
console.log(details)
// window.location.href = '/pages/sucess';
})
.catch(function(error) {
// redirect to failed page if internal error occurs
window.location.href = '/pages/fail?reason=internalFailure';
});
}else{
window.location.href = '/pages/fail?reason=failedToCapture';
}
});
},
Laravel route
Route::get('pages/sucess', [App\Http\Controllers\OrderController::class, 'sucess'])->name('pages.sucess');
Output
{
"id": "9S369747UW261581G",
"intent": "CAPTURE",
"status": "COMPLETED",
"purchase_units": [
{
"reference_id": "default",
"amount": {
"currency_code": "USD",
"value": "0.50"
},
"shipping": {
"name": {
"full_name": "John Doe"
},
},
"payments": {
"captures": [
{
"id": "5DS42883V93959154",
"status": "COMPLETED",
"amount": {
"currency_code": "USD",
"value": "0.50"
},
}
]
}
}
],
}
How can one capture PayPal response details and dump the same detail to the Laravel controller so that it can be saved to the database?
Do not use actions.order.create() / .capture() on the client side and then post information to a database.
Instead, change to a proper server-side integration. Make two routes on your server, one for 'Create Order' and one for 'Capture Order', documented here. These routes should return only JSON data (no HTML or text). When a capture response is successful, store its resulting payment details in your database (particularly purchase_units[0].payments.captures[0].id, the PayPal transaction ID) and perform any necessary business logic (such as sending confirmation emails or reserving product) before sending your return JSON.
Pair those two routes with the following approval flow: https://developer.paypal.com/demo/checkout/#/pattern/server

Docusign not hitting webhook URL

I'm trying to use webhooks, but no events are being sent to my application via the webhook url. So far I was able to configure and send correctly envelopes with enough information to monitor status, but when things changes in the envelopes, nothing happens, I mean, no requests are made to my webhook URL, at all.
My app is doing good, so if I manually hit (GET) https://subdomain.app.com/docusign/webhook, it works fine and it shows both on my app log and Nginx log. But viewing, signing and completing documents/envelopes are not generating events to the webhook url.
I noticed that, in the examples, the events are capitalized for recientEvents, but not for envelopeEvents, is this right?
Is there anything else to be configured?
Is is possible to see this information in the Docusign web interface (https://account-d.docusign.com/logout#/username)? I would like to check if this data is correctly set in the envelope.
Here's the envelope request (minus some data):
{
"documents": [{
"documentId": 1,
"name": "XXXXXXXXX.pdf",
"documentBase64": "XXXXXXX"
}],
"recipients": {
"signers": [{
"tabs": {
"signHereTabs": [{
"documentId": 1,
"recipientId": 1,
"pageNumber": 1,
"anchorString": "recipient_signature"
}]
},
"name": "XXXXXX",
"email": "XXXX#XXXX.co",
"recipientId": 1,
"clientUserId": XXXX
}]
},
"eventNotification": {
"url": "https:\/\/subdomain.app.com\/docusign\/webhook",
"loggingEnabled": "true",
"envelopeEvents": [{
"envelopeEventStatusCode": "sent"
}, {
"envelopeEventStatusCode": "delivered"
}, {
"envelopeEventStatusCode": "completed"
}, {
"envelopeEventStatusCode": "declined"
}, {
"envelopeEventStatusCode": "voided"
}, {
"envelopeEventStatusCode": "sent"
}, {
"envelopeEventStatusCode": "sent"
}],
"recipientEvents": [{
"recipientEventStatusCode": "Sent"
}, {
"recipientEventStatusCode": "Delivered"
}, {
"recipientEventStatusCode": "Completed"
}, {
"recipientEventStatusCode": "Declined"
}, {
"recipientEventStatusCode": "AuthenticationFailed"
}, {
"recipientEventStatusCode": "AutoResponded"
}]
},
"status": "sent",
"emailSubject": "XXXXXX",
"brandId": "XXXXXXXXXX"
}
EDIT:
Entering Connect -> Log/Failures looks like the system is not really performing as it should, because sometimes I get
And some other times I get an empty list. Going in the publish option, when it's working I get a list of documents/envelopes, and I see the last envelope I sent there, which looks fine.
You can view your recent connect logs/failures at the Docusign Admin web application. See instructions to use the Admin site here
If your connect messages were not sent, to the listener URL you provided, they should show up in the failures section.
API : You can also view your connect logs/failure using the connectEvents api's
Here is some documentation for troubleshooting connect issues.
The capitalization of status codes is not an issue. They are case insensitive.

Categories