Introduction
I´ve been looking at integrating a somewhat big project of mine with PayPal Express Checkout, and use that as the main system to handle payments on my site. Although their documentation is quite extensive, I really can´t find too good a place to start. Over the past few days, I´ve been presented with headache upon headache, in attempt to figure out how on Earth to integrate my site, as well as its database, with PayPal Express Checkout.
Summary
The JavaScript bit is pretty straight forward, and charging money using the client-side integration is extremely easy. But I also need a way to update fields in my database upon a payment going through (being successful), and from what I´ve understood so far, that can only be done by using the advanced server integration.
Problem
The problem, given the aforementioned reasons, is somehow managing to implement the advanced server integration solution, into my platform, in order to make way for changes in the database to occur upon a transaction being successful.
As mentioned before, the PayPal documentation is fairly extensive--problem is, I´ve never really went over using REST API´s, and it appears as though integrating what I´m after can only really be done using the PayPal REST API, to verify that the payment(s) have taken place.
Question
With all that being said, what I need help with is finding some place in all of this to start. I´m aware StackOverflow may not be the best place for a question like this, but it appears lots of people are having trouble with this exact question, and it´s an essential part of settings up any business on the web to get this to work.
Any pointers on where to start, or anything relating to the subject other than the PayPal documentation would be extremely helpful.
What follows is a minimal way to use Express Checkout with a server-side Payments REST API integration, including the associated code for client and server in JavaScript. I hope it helps.
You'll need to login to developer.paypal.com and create a REST API app. Your new app will be assigned a Client ID and Secret which you can use to request access tokens, which authorize your use of the PayPal REST API. The only app setting you need for what follows here is "Accept Payments". Sandbox accounts for testing are automatically created when you create an app.
Your (checkout) page loads PayPal's Express Checkout script, and a script you write renders a PayPal button:
// In your script:
paypal.Button.render({
env: 'sandbox', // Or 'production'.
commit: true, // Show 'Pay Now' button.
style: { // Style the button.
size: 'responsive',
color: 'silver',
shape: 'rect'
},
payment: function(data, actions) {
// See step 3.
},
onAuthorize: function(data, actions) {
// See step 7.
}
}, '#paypal-button');
Your customer clicks the rendered PayPal checkout button.
The payment function you define in the argument you give to paypal.Button.render() is called by PayPal's script, which sends a request to your server with your payload. Your payload contains (for example) cart contents and its associated data.
paypal.Button.render({
// ...
payment: function(data, actions) {
return paypal.request(
{
method: 'post',
url: '/your-api/create-payment',
json: {
order: items,
or: whatever
}
}
).then(
function(res) {
// Return the payment id received from your server.
return res.paymentId;
}
).catch(
function(err) {
// Oops, foobared.
}
);
},
// ...
}
Your server sends a request to api.sandbox.paypal.com, with your access token, to create a payment. When your server receives the newly created payment data from PayPal it (perhaps) persists the data on your server, but it must return the payment id to the client.
// HTTP request data for creating a payment.
{
method: 'post',
// Remove '.sandbox' below to use production endpoint.
url: 'https://api.sandbox.paypal.com/v1/payments/payment';
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + 'your access token here'
},
data: {
// Allows immediate payments with PayPal and credit cards in
// the Express Checkout dialog.
intent: 'sale',
// I suppose these were required by other PayPal REST services,
// but they won't be used because, for Express Checkout, you
// handle the confirmation/cancellation flow yourself, on the
// client.
redirect_urls: {
return_url: 'https://...'
cancel_url: 'https://...'
},
payer: {
payment_method: 'paypal',
},
note_to_payer: 'Thanks, you\'re magnificently awesome!',
transactions: [{
amount: {
total: total, // Your computed total.
currency: 'USD',
details: {
subtotal: subtotal, // Your computed subtotal.
tax: tax, // Your computed tax.
shipping: shipping // Your computed shipping.
// Other parameters are available for your use.
}
},
item_list: {
items: [
{
name: 'shinny shirt of mithril mail',
description: 'shinny',
sku: '12345',
quantity: 1,
price: 1.00,
currency: 'USD'
}
],
// Some properties aren't required, like this one.
shipping_method: 'USPS'
},
description: 'Your PayPal payment for a shinny shirt of mithril mail.',
// Not required and can be added later by patching the
// payment: maybe you don't want to add an invoice to and
// order until the payment is approved.
invoice_number: 12345
}]
}
}
The payment function you provided then receives the payment id from your server and uses it as its return value.
// See return value in step 3.
And now the Express Checkout payment dialog pops up for your customer, who must login in and confirm the payment, create an account, or checkout with a credit/debit card.
After the customer confirms payment by clicking the Pay Now button in the Express Checkout dialog, the onAuthorize function you define in the argument you give to paypal.Button.render() is called by PayPal's script. You can use this function's actions parameter to get() payment information from PayPal, which you can use in a confirmation page, etc. Use its data parameter to get the payment id and payer id, which must be sent in a request to your server to execute the payment.
onAuthorize: function(data, actions) {
return actions.payment.get().then(function(paymentDetails) {
// Get at the payment details like this...
// paymentDetails.payer.payer_info.first_name;
// paymentDetails.payer.payer_info.shipping_address.city;
// paymentDetails.payer.payer_info.shipping_address.state;
var payload = {
paymentId: data.paymentID,
payerId: data.payerID
};
return paypal.request(
{
method: 'post',
url: '/your-api/execute-payment',
json: payload {
paymentId: data.paymentID,
payerId: data.payerID
}
}
).then(
function(res) {
// Gotten paid! Show confirmation page.
}
).catch(
function(err) {
// Dang it.
}
);
});
}
Your server receives the payment id and payer id from the client and makes another request to PayPal, this time to execute the payment. The transaction is complete if the execute request returns successfully. And now you can persist (or update) the payment data on your server.
// HTTP request data for executing a payment.
{
method: 'post',
// Remove '.sandbox' below to use production endpoint.
url: 'https://api.sandbox.paypal.com/v1/payments/payment/' + paymentId + '/execute/',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + 'your access token here'
},
data: {
payer_id: payerId
}
}
The client's latest request to your server completes successfully and you show a confirmation page, optionally with payment info from step 7 or from your server.
As for a place to start, here's what I suggest:
Automate requesting and storing your access token on your server. You'll need to store it because PayPal rate-limits token requests. So, either automate keeping it fresh or request a new one whenever you need to. I believe they expire in eight or nine hours; in any case, the expires-in time is given to you with the token. https://developer.paypal.com/docs/api/overview/
Build the /your-api/create-payment server endpoint. Test it with requests from an API tool like Postman using your access token. When everything goes well your server will make a successfull call to PayPal to create a payment, and you can explore the data in the response. It doesn't matter that you don't plan to execute the payment...it's the sandbox after all. Then build in your server-side persistence (if you want) and return the payment id to the client.
Next, load the PayPal script and your checkout-button rendering script on your site, and see if the Express Checkout login appears when you click the checkout button. If an error occurs in the create-payment process you'll see an error rather than the Express Checkout login.
When your system is creating payments and you can log in at the Express Checkout dialog with your sandbox credentials, build the /your-api/execute-payment endpoint. When your server has a working execute-payment endpoint you should be able to log in with Express Checkout and use your sandbox buyer account to complete a payment.
If after all that you think you'll have a go, the docs you want to pay attention to are https://developer.paypal.com/docs/api/ > API Reference > Payments API > Payments. There are five sections: create, execute, show, update and list. Read them. It will help.
Related
I currently have an Opayo (SagePay) Server integration where I'm apparently spoilt by an ability to simply respond to a payment notification callback with an ERROR status to reject/void a transaction if there is a problem fulfilling an order on my side. I think, there is some sort of automatic authorise then capture happening behind the scenes, where a payment is only captured if a success/OK status is returned by my notification handler.
I'm looking at how I can achieve the same with a Stripe checkout integration (Pre-built Checkout, PHP with webhooks) and it seems that I need to to use authorisation and manual capturing:
$session = \Stripe\Checkout\Session::create([
...
'payment_intent_data' => [
'capture_method' => 'manual',
],
]);
In the sample code and documentation (which is generally excellent, but I feel falls short here), it shows how to create a webhook to handle the completion of the checkout session:
// Handle the checkout.session.completed event
if ($event->type == 'checkout.session.completed') {
$session = $event->data->object;
// Fulfill the purchase...
fulfill_order($session);
}
Curiously, the payment_status of the transaction is not checked and the example suggests you would simply fulfil your order here. I can only assume, if you are not using a delayed payment method then you can assume the payment is successful at this point, although it seems a dangerous assumption to make.
Further along in the documentation, under "Handle delayed notification payment methods Server-side", we have a fuller example, where the payment status is actually checked before fulfilling the order:
switch ($event->type) {
case 'checkout.session.completed':
$session = $event->data->object;
// Save an order in your database, marked as 'awaiting payment'
create_order($session);
// Check if the order is paid (e.g., from a card payment)
//
// A delayed notification payment will have an `unpaid` status, as
// you're still waiting for funds to be transferred from the customer's
// account.
if ($session->payment_status == 'paid') {
// Fulfill the purchase
fulfill_order($session);
}
...
If I am using the manual capture method, how exactly should the code above be used/adapted? Presumably the payment_status will not be paid so what do I check for and how do I access the PaymentIntent to be able to capture it? Should this all happen in the checkout.session.completed event?
A code example would be great; unfortunately there is only this footnote in the documentation:
To capture an uncaptured payment, you can use either the Dashboard or the capture endpoint. Programmatically capturing payments requires access to the PaymentIntent created during the Checkout Session, which you can get from the Session object.
I have checked the online code samples but this exact scenario isn't covered.
EDIT: Adding below my understanding of the flow I need with questions:
switch ($event->type) {
case 'checkout.session.completed':
$session = $event->data->object;
if ($session->payment_status == 'unpaid') {
// We need to capture the payment if we have arrived here?
// Is this the only scenario where we would end up here?
// How do we get the PaymentIntent, what status if any should we check on it?
// Attempt to fulfil the purchase here, if all good then
// capture the payment
} else if ($session->payment_status == 'paid') {
// Assume this was not an auth/capture type payment?
// There is no other scenario where we would end up here?
}
...
If you receive the checkout.session.completed event, then the synchronous payment method completed successfully.
For async payment methods, there are other events:
checkout.session.async_payment_succeeded
checkout.session.async_payment_failed
I'm not sure why you'd use auth and capture here, since the payment is successful either upon receipt of checkout.session.completed or checkout.session.async_payment_succeeded, but if you need to, you'd set the option to do so here and then you'd need to capture the Payment Intent associated with the Checkout Session.
If you need to determine if the Charge was captured or not, you'd need to retrieve the Payment Intent and look a the Charge's captured value: https://stripe.com/docs/api/charges/object#charge_object-captured
I'm having trouble trying to verify paypal webhook signatures with PHP. Using the new V2 of paypals APIs I am receiving the paypal webhook on my page.
However I can not seem to successfully validate the signature.
From the link HERE I got some sample webhook validation PHP code from paypal.
I can not get it working, I don't know where I am supposed to get bootstrap.php from in the paypal code. The paypal information seems incomplete or half baked. Paypal seems to be terrible to set-up compared to Stripe.
Has anyone got experience of validating paypal webhook signatures with PHP when using V2 of the paypal APIs ?
Well I have come to the conclusion that the Paypal developer information is rather poor, it is scatted all over the place, on multiple different pages and sites. The examples they give on the paypal developer website HERE are not a complete picture of what is required to validate a webhook signature. Stripe developer documentation is much better formatted and concise.
Installing Paypal Checkout V2 SDK does not give you the necessary development tools to validate paypal webhook signatures i.e. you can process payments and receive webhooks but you can not validate the webhook signatures....I know stupid. Tip do not download the SDK directly as you will not include the required autoload.php file. Use composer to install Paypal Checkout V2 SDK so that you get the autoload.php file.
Once you are to a point that you can process payments and receive webhooks form paypal you need to install the another SKD called Paypal Rest API SDK. Again use composer to install the SDK so that you get a autoload.php file which you will need.
When you install Paypal Rest API SDK amazingly you will still be missing files that are required to validate the payapl webhook signatures. I can find no mention of these anywhere on the paypal developer website.
bootstrap.php & common.php
Thanks to #Grumpy I got some samples provided on github HERE
Note you will probably need to modify the samples a bit in order to get them working with your website. Tip set the logger to false and save yourself some bother if you don't have the necessary access permissions to write.
Once you have bootstrap.php & common.php files created you can write the code for your webhook endpoint page i.e. the page that paypal sends the webhook to. I have included my PHP code below for how to validate and then process the paypal webhook. Tip in the below code you need to specify the webhook ID, each webhook that you create in paypal has a unique ID. Also when you are testing you can not use webhook simulator as this will fail validation, you can make manually a payment with your sandbox account details which will trigger a webhook payment event.
Paypal sure don't make it easy, their documentation is all over the place compared to Stripe. The Paypal webhooks can sometimes take several minutes to arrive after a payment is made, very frustrating when trying to debug. Also it's a bit ridiculous that they have a webhook simulator on the paypal developer website that can not be used to validate signatures...if stripe can do it why can't paypal.
<?php
//get the webhook payload
$requestBody = file_get_contents('php://input');
//check if webhook payload has data
if($requestBody) {
//request body is set
} else {
//request body is not set
exit();
}
use \PayPal\Api\VerifyWebhookSignature;
use \PayPal\Api\WebhookEvent;
$apiContext = require __DIR__ . '/bootstrap.php';
//Receive HTTP headers that you received from PayPal webhook.
$headers = getallheaders();
//need header keys to be UPPERCASE
$headers = array_change_key_case($headers, CASE_UPPER);
/*
example header paypal signature content for webhook, these values are recieved as an array, we then need to use this data to verify the payload
CONTENT-LENGTH : 1376
CORRELATION-ID : 6db85170269e7
USER-AGENT : PayPal/AUHD-214.0-54377828
CONTENT-TYPE: application/json
PAYPAL-AUTH-ALGO : SHA256withRSA
PAYPAL-CERT-URL : https://api.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a784-5edc0ebc
PAYPAL-AUTH-VERSION : v2
PAYPAL-TRANSMISSION-SIG : Hc2lsDedYdSjOM4/t3T/ioAVQqFPNVB/AY/EyPNlavXk5WYUfnAmt9dyEP6neAPOjFHiVkXMK+JlLODbr6dalw6i26aFQdsPXqGl38Mafuu9elPE74qgsqNferUFgHi9QFXL+UZCNYcb4mvlDePXZIIAPbB0gOuFGOdEv2uqNwTCSAa/D8aguv1/51FWb3RkytFuVwXK/XNfIEy2oJCpDs8dgtYAZeojH8qO6IAwchdSpttMods5YfNBzT7oCoxO80hncVorBtjj1zQrkoynEB9WNNN9ytepNCkT8l29fQ4Sx/WRndm/PESCqxqmRoYJoiSosxYU3bZP7QTtILDykQ==
PAYPAL-TRANSMISSION-TIME : 2020-04-05T14:40:43Z
PAYPAL-TRANSMISSION-ID : 6dec99b0-774b-11ea-b306-c3ed128f0c4b
*/
//if any of the relevant paypal signature headers are not set exit()
if(
(!array_key_exists('PAYPAL-AUTH-ALGO', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-ID', $headers)) ||
(!array_key_exists('PAYPAL-CERT-URL', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-SIG', $headers)) ||
(!array_key_exists('PAYPAL-TRANSMISSION-TIME', $headers))
)
{
exit();
}
//specify the ID for the webhook that you have set up on the paypal developer website, each web hook that you create has a unique ID
$webhookID = "ENTER_YOUR_WEBHOOK_ID_HERE";
//start paypal webhook signature validation
$signatureVerification = new VerifyWebhookSignature();
$signatureVerification->setAuthAlgo($headers['PAYPAL-AUTH-ALGO']);
$signatureVerification->setTransmissionId($headers['PAYPAL-TRANSMISSION-ID']);
$signatureVerification->setCertUrl($headers['PAYPAL-CERT-URL']);
$signatureVerification->setWebhookId($webhookID);
$signatureVerification->setTransmissionSig($headers['PAYPAL-TRANSMISSION-SIG']);
$signatureVerification->setTransmissionTime($headers['PAYPAL-TRANSMISSION-TIME']);
$signatureVerification->setRequestBody($requestBody);
$request = clone $signatureVerification;
try {
$output = $signatureVerification->post($apiContext);
} catch (Exception $ex) {
//error during signature validation, capture error and exit
ResultPrinter::printError("Validate Received Webhook Event", "WebhookEvent", null, $request->toJSON(), $ex);
exit(1);
}
$sigVerificationResult = $output->getVerificationStatus();
// $sigVerificationResult is a string and will either be "SUCCESS" or "FAILURE"
//if not webhook signature failed validation exit
if($sigVerificationResult != "SUCCESS"){
exit();
}
else if($sigVerificationResult == "SUCCESS"){
//paypay webhook signature is valid
//proceed to process webhook payload
//decode raw request body
$requestBodyDecode = json_decode($requestBody);
//pull whatever info required from decoded request body, some examples below
$paymentSystemID = $requestBodyDecode->id;
$eventType = $requestBodyDecode->event_type;
//do something with info captured from the webhook payload
}
This seems to work with php 8.1:
if (openssl_verify(
data: implode(separator: '|', array: [
$httpPayPalTransmissionId,
$httpPayPalTransmissionTime,
$webhookID,
crc32(string: $rawRequestBody),
]),
signature: base64_decode(string: $httpPayPalTransmissionSignature),
public_key: openssl_pkey_get_public(public_key: file_get_contents(filename: $cachedHttpPayPalCertUrl)),
algorithm: 'sha256WithRSAEncryption'
) === 1) {
die('OK');
} else {
die('FAILED');
}
hello I am using core PHP in client Project, I need to integrate rave flutter wave API, I have searched a lot of links
now I am checking this URL here but not getting a good result,
so please help me in rave flutter wave "Nigeria payment gateway" API in PHP
Our Product for collections is called Rave, please follow the steps below to get started:
Sign up https://ravepay.co/ , for a sanbox account sign up on http://rave.frontendpwc.com
Add a settlement account on sign up
Select a settlement preference by navigating to Account settings > Settlement preference (Find documentation attached for more detailed instruction).
Documentation for developers: https://flutterwavedevelopers.readme.io/v1.0
Pay Now
document.addEventListener("DOMContentLoaded", function(event) {
document.getElementById("submit").addEventListener("click", function() {
var chargeResponse = "",
trxref = "FDKHGK" + Math.random(),// add your transaction ref here
pubkey = "XXXX"; // Add public keys generated on your dashboard here
getpaidSetup({
customer_email: "xxxxx",//
amount: 1000,
currency: "NGN",
country: "NG",
custom_logo: "http://imgur.com/uuRnkV9",
custom_description:"",
custom_title: "The Start",
txref: trxref,
PBFPubKey: pubkey,
onclose: function(response) {},
callback: function(response) {
//flw_ref = response.tx.flwRef;
console.log("This is the response returned after a charge", response);
if(response.tx.chargeResponse =='00' || response.tx.chargeResponse == '0') {
// redirect to a success page
} else {
// redirect to a failure page.
}
}
});
});
});
If you have any issues at any point, please let us know and we will help sort you out.
If you are here in 2020, the docs for the payment gateway has been updated, you can check how to integrate this with laravel seamlessly using this link
https://questionbump.com/question/how-can-i-setup-flutterwave-payment-webhook-successfully-on-laravel-5/
I hope it helps someone. I struggled to get this working.
I've been searching about this and everyone seems to say that to refund, you must go to paypal website, etc. then the customer will issue a refund there.
What I need is this:
When the customer wants to refund, an approval will be sent to admin in my website, then when the admin approves, the admin will, inside the website, select the amount of money to payback the customer. (Then I guess, it will direct to paypal and type the email and password?)
Is this possible? That every process is in my website except for the paying? I didn't seem to find solutions, I don't have any lead also. Thanks.
Absolutely Possible,
First of all do make a refund request you must store Paypal's Transaction ID
Once Your admin clicks refund approval button
var refundTransactionRequestType = new RefundTransactionRequestType
{
Version = RefundTransactionRequestVersion, //it must be same version with Express Checkout Request Version
RefundTypeSpecified = true,
TransactionID = transactionId //HERE You need to set Paypal's TransactionID
};
var refundTransactionRequest = new RefundTransactionRequest(
_customSecurityHeaderType, //CustomSecurityHeaderType will have your paypal merchant credentials
new RefundTransactionReq //Your RefundTransactionRequestType
{
RefundTransactionRequest = refundTransactionRequestType
});
RefundTransactionResponse refundTransactionResponse;
try
{
PayPalAPIInterface apiInterface = new PayPalAPIInterfaceClient();
refundTransactionResponse = apiInterface.RefundTransaction(refundTransactionRequest);
}
catch (FaultException err)
{
return 123123;//Error CODE
}
The Code is for C# but it will have the same logic
Here is reference for RefundTransaction SOAP
Here is reference for RefundTransaction NVP
Working with the PayPal API and using the Name-Value Pair Interface PHP source codes from SDKs and Downloads: Simplify Integrations with Downloads and SDKs.
My question is similar to "Removing (or prefilling) the address details for PayPal Express Checkout" but I don't want shipping cost/address or anything related about shipping at all.
I keep all shipping details on my system (even sometimes shipping doesn't even apply and there is no charge for it) and I just want user to pay through PayPal without shipping address and shipping cost.
How can I disable shipping part of checkout?
If you're using the newer API, you could also pass NOSHIPPING=1 (not no_shipping)
Further details about all possible parameters to the SetExpressCheckout here:
https://developer.paypal.com/docs/nvp-soap-api/nvp/
Or lookup for Payment experience in new REST API
For others looking for this, because PayPals documentation is so GREAT (cough, cough).
NO web experience profile REQUIRED!
Using REST API V2 with Javascript/JQuery and turning "Ship To" off for an ORDER, here is the correct code example:
createOrder: function(data, actions) {
$('#paypalmsg').html('<b>' + 'WAITING ON AUTHORIZATION TO RETURN...' + '</b>');
$('#chkoutmsg').hide()
return actions.order.create({
purchase_units: [{
description: 'GnG Order',
amount: {
value: cartTotal
}
}],
application_context: {
shipping_preference: 'NO_SHIPPING'
}
});
},
Hey Ergec, just pass along the no_shipping parameter with a value of 1.
From PayPal's documentation:
no_shipping
Do not prompt payers for shipping address. Allowable values:
0 – prompt for an address, but do not require one
1 – do not prompt for an address
2 – prompt for an address, and require one
The default is 0.
The current right answer is depracated. To fix the issue in new API we should create Payment web experience profile resource with needed parameters and attach it to request Payment .
Example in PHP:
/** Note: Define some variables yourself. */
$inputFields = new InputFields();
$inputFields->setAllowNote(true)
->setNoShipping(1) // Important step
->setAddressOverride(0);
$webProfile = new WebProfile();
$webProfile->setName(uniqid())
->setInputFields($inputFields)
->setTemporary(true);
$createProfile = $webProfile->create($apiContext);
$payment = new Payment();
$payment->setPayer($payer);
$payment->setIntent($intent);
$payment->setRedirectUrls($redirectUrls)
$payment->setTransactions(array($transaction));
$payment->setExperienceProfileId($createProfile->getId()); // Important step.
$payment->create($apiContext);
if ($payment->getState() === "created") {
$approvalLink = $payment->getApprovalLink()
header("Location: $approvalLink"); // Redirects user to PayPal page.
}
Note: You can find all above used classes by link: https://github.com/paypal/PayPal-PHP-SDK/tree/master/lib/PayPal/Api
To solve for this from current (2019) web client .JS, add the application_context block to the request body.
Below is an example for createSubscription() call; and I'm thinking this will work with createOrder() as well
paypal.Buttons({
createSubscription: function (data, actions) {
return actions.subscription.create({
'plan_id' : 'P-123',
'application_context': {
'shipping_preference': 'NO_SHIPPING'
}
});
},
onApprove: function (data, actions) {
// ...
}
})
.render('#paypal-button-container');
Thanks to the example code here:
https://developer.paypal.com/docs/subscriptions/integrate/#4-create-a-subscription
Here's where the field enums are listed:
https://developer.paypal.com/docs/classic/api/merchant/SetExpressCheckout-API-Operation-NVP/
Create a web profile based on the example found in the API: CreateWebProfile.php.
$createProfileResponse = require __DIR__ . '/CreateWebProfile.php';
$payment = new Payment();
$payment->setExperienceProfileId($createProfileResponse->getId());
File path: paypal/rest-api-sdk-php/sample/payment-experience/CreateWebProfile.php
2022 and beyond
The documentation for V2 of the PayPal API, currently the latest version, and which seems to have been cleaned up a lot over the past year or so, states the following:
shipping_preference
Displays the shipping address to the customer.
Enables the customer to choose an address on the PayPal site.
Restricts the customer from changing the address during the payment-approval process.
The possible values are:
GET_FROM_FILE. Use the customer-provided shipping address on the PayPal site.
NO_SHIPPING. Redact the shipping address from the PayPal site. Recommended for digital goods.
SET_PROVIDED_ADDRESS. Use the merchant-provided address. The customer cannot change this address on the PayPal site.
Therefore, simply adding:
"application_context" => [
"shipping_preference" => "NO_SHIPPING"
]
or if using JavaScript:
"application_context": {
"shipping_preference" => "NO_SHIPPING"
}
...to your order creation should disable any shipping options.
As an example, my PHP code to create an order using PayPal's Standard Checkout Integration (which in turn makes use of the Smart Buttons) now looks like this:
$order = $paypal->createOrder([
"intent"=> "CAPTURE",
"purchase_units"=> [
[
"amount"=> [
"currency_code" => "GBP",
"value"=> 4.99
],
'description' => 'My product description',
],
],
"application_context" => [
"shipping_preference" => "NO_SHIPPING"
]
]);
#Ergec : I tried this:
$nvpstr = "&ADDRESSOVERRIDE=1".$shiptoAddress."&L_NAME0=".$L_NAME0."&L_NAME1=".$L_NAME1."&L_AMT0=".$L_AMT0."&L_AMT1=".$L_AMT1."&L_QTY0=".$L_QTY0."&L_QTY1=".$L_QTY1."&MAXAMT=".(string)$maxamt."&ITEMAMT=".(string)$itemamt."&AMT=".$itemamt."&ReturnUrl=".$returnURL."&CANCELURL=".$cancelURL."&CURRENCYCODE=".$currencyCodeType;
It works. Here we can also use shipping address even though we are not charging any amount.