- Definition
- Architecture
- Front-end integration guide
- Local debug
- Tests
- How to use
- HTTP Responses
- Possible edge cases
- How to encrypt/decrypt files
- Build and deploy
In this process, there are 3 parties involved:
- The frontend - the browser part of the shop. This is what the user interacts with.
- The backend - the shop server.
- Paypal-integration - hosted service (this repository) which exposes public endpoints
-
Shop backend creates a payment in CT platform and obtains payment ID.
-
Shop backend sends POST request with the obtained payment ID to following Create API of paypal-integration.
http://paypal-plus-integration-server.com/${tenantName}/commercetools/create/payments/${ctpPaymentId}
For details, please see the section 1.1 of How to use
-
Shop backend updates user's details by posting the payment object to following Patch API of paypal-integration.
http://paypal-plus-integration-server.com/${tenantName}/commercetools/patch/payments/${ctpPaymentId}
For details, please see the section 1.1 of How to use
-
Shop front-end trigger Paypal-plus checkout function, customer is redirected to payment authorization page. Once completed, it returns a success URL which redirects the customer back to order confirmation page in the shop front-end.
-
After order confirmation, shop front-end extracts payer ID and Paypal payment ID, and send them to shop back-end. Back-end sends POST request to following Execute API of paypal-integration.
http://paypal-plus-integration-server.com/${tenantName}/commercetools/execute/payments/
The request of above API should be with payload as follow :
{"paypalPlusPaymentId": "${paymentId}", "paypalPlusPayerId": "${payerId}"}
See commercetools Paypal Plus Service Integration Guide documentation.
./gradlew bootRun
Local Run/Debug setting may be configured in /src/main/resources/config/application.yml
file. If the file exists - it overrides default /src/main/resources/application.yml
.
Local integration test setting may be configured in /src/test/resources/config/application.yml
file. If the file exists - it overrides main context config and config from /src/test/resources/application.yml
.
You must include 2 different CT client credentials and paypal_plus client credentials in order to make the integration tests pass successfully.
Note: keep this settings different from /src/main/resources/config/application.yml
,
which is used for local run/debug, because the integration tests will remove all data form the CTP project
-
Show available PayPal payment methods
-
Backend creates CTP payment and assigns it to the cart.
-
Required fields for the CTP cart:
- There must be at least 1 line or custom line item defined
- Cart total amount should be > 0
- Shipping address should be set
-
Required fields for the CTP payment:
amountPlanned
cancelUrl
(custom field)successUrl
(custom field)reference
(custom field, up to 116 characters)languageCode
(custom field)paymentMethodInfo
needs to be set like this:
"paymentMethodInfo": { "paymentInterface": "PAYPAL_PLUS", "method": "paypal" }
-
Optional fields for the CTP payment:
-
experienceProfileId
(custom field): if the payment should be supplied with certain Paypal Plus Experience Profile Id.Note: looks like there is no certain clarity in PayPal Plus documentation/support how the web experience profiles should be used, and potentially they will be deprecated in the future, so we recommend not to use them unless you are completely sure what you achieve using them. If you need prevent address change by customers (buyers) - use
shippingPreference
below. -
shippingPreference
(custom field): value ofPayment#application_context#shipping_preference
enum. This value is used to allow/block shipping address change by the customer (buyer). Reed more at Application Context DocumentationNote: So far this feature is not properly documented by Paypal API developers, so the reference above actually refers to Orders API, instead of Payments API. Respective issues are created:
-
description
(custom field): if specified -Payment#transaction#description
field is set to this value. Otherwise the description falls back to the following string:Reference: ${payment.custom.fields.reference}
Note: Maximum allowed length according to PayPal Plus API:
127
(includingReference:
prefix in case of fallback, so thereference
custom field must be up to 116 characters long)
-
-
-
Backend POSTs CTP payment ID created in the previous step to Paypal-integration. Example:
POST http://paypal-plus-integration-server.com/${tenantName}/commercetools/create/payments/${ctpPaymentId}
If request was successful both response body and CTP payment object will have
approvalUrl
defined. -
Frontend uses returned
approvalUrl
to render available payment methods as described in the Paypal Plus integration documentation.
-
-
Add user's addresses to Paypal Plus
- Before redirect the user to Paypal, backend POSTs CTP payment ID to Paypal-integration:
POST http://paypal-plus-integration-server.com/${tenantName}/commercetools/patch/payments/${ctpPaymentId}
- NOTICE: frontend developers should refer to the newest version of Paypal Plus Integration documentation to know how to make a request
before redirect in Javascript. As of August 2017, on submit should call
ppp.doContinue()
. Additionally,ppp
object must be created with the following option:
var ppp = PAYPAL.apps.PPP({ onContinue: function () { $.post("/url-to-your-shop-that-will-make-call-to-paypal-integration", function (data) { if (data != false) { ppp.doCheckout(); } }); } });
- Before redirect the user to Paypal, backend POSTs CTP payment ID to Paypal-integration:
-
Execute payment after user successfully finished PayPal Plus checkout and was redirected back to the shop through
successUrl
. PayPal Plus will set 3 request parameters tosuccessUrl
:token
paymentId
- identifies this particular payment. Required for execute payment.PayerID
- identifies the particular payer. Required for execute payment.
Example of
successUrl
returned by PayPal Plus:http://example.com/checkout/payment/success?paymentId=${paymentId}&token=${token}&PayerID=${payerId}
-
It is strongly recommended to compare the payment from Paypal to payment from CTP to see if there were any changes during the payment process. Example of how this can happen is described here. The possible changes could be:
- User's shipping address has changed
- Cart total amount has changed
Backend GET Paypal payment by
paypalPaymentId
:GET http://paypal-plus-integration-server.com/${tenantName}/paypalplus/payments/${paypalPaymentId}
The Paypal Plus payment object will be returned in
payment
as JSON like this:{"payment":"{\"id\":\"PAY-xxx\",\"intent\":\"sale\",\"cart\":\"1234abcd\", .... }"}
-
Backend extracts PayPal specific parameters:
paymentId
,PayerID
and POSTs them in the request body to Paypal-integration for payment execution. Example:POST http://paypal-plus-integration-server.com/${tenantName}/commercetools/execute/payments/ {"paypalPlusPaymentId": "${paymentId}", "paypalPlusPayerId": "${payerId}"}
-
In case of invoice payment, the bank details for the invoice will be saved as custom fields in the Payment object. Example:
{ "custom": { "type": { "typeId": "type", "id": "1455d4e6-41b4-yyyy-xxxx-4f120864e231" }, "fields": { "reference": "6KF07542JV235932C", "description": "Thank you for your order. Order number: 12345", "paymentDueDate": "2017-09-27", "amount": { "centAmount": 200, "currencyCode": "EUR" }, "paidToIBAN": "DE1212121212123456789", "paidToAccountBankName": "Deutsche Bank", "paidToAccountHolderName": "PayPal Europe", "paidToBIC": "DEUTDEDBPAL" } } }
All endpoints accept and return data as JSON.
-
Return HTTP codes on
create/payments
endpoint URL:- 201: successfully created payment in PayPal and CTP updated with approvalUrl as custom field
-
Return HTTP codes on
execute/payments
endpoint URL:- 201: successfully executed payment in PayPal, created transaction in CTP
-
Return HTTP codes on
paypalplus/payments/${paypalPaymentId}
endpoint URL:- 200: Paypal payment found and returned as JSON response body
-
Common error codes
- 404: resource not found by the supplied UUID/ID
- 400: required request parameters are missing or wrong
- 503: any exception which implies that request can be safely retried with the same parameters/payload again
- 500: unexpected/not handled exceptions
Additionally, response can contain additional response body. All fields of the response body are optional. Example:
{
"approvalUrl": "https://test.de", # applicable only in case of create payment
"errorCode": "", # only in case of error and represents a unique error code
"errorMessage": "Parameter 'x' is missing" # only in case of error
"payment": {"id":"XXX", "intent":"sale", ...} # only in case of getting the payment object
}
-
First case:
- user inputs an address Berlin, Germany
- he clicks on Continue and is redirected to Paypal payment page with this address.
- In a different tab, the same user changes his address to Paris, France.
- The user confirms the payment in the first tab and is redirected back to shop
- Paypal has the address of Berlin, but the shop will deliver the goods to Paris.
Possible solution: the backend calls
patch/payments
endpoint every time user changes it. -
Second case:
- user has cart with item1=10€
- user is redirected to Paypal payment page
- in a different tab, user changes his cart to e.g. item2=20€
- user completes the payment in the first tab and is redirected back to shop
- paypal approves the payment for 10€, but the real total amount of sold (shipped) items has changed to 20€
Possible solution: the backend has to compare total amount of the payment and total amount of payment's cart before calling
execute/payments
endpoint. In case of differences, the whole payment process must be restarted.
-
Request Professional Services team to add you as an encryptor/decryptor to
ps-keyring#commercetools-paypal-plus-integration
key. -
Navigate to
root
directory. -
Run the following commands to:
-
Encrypt
build-settings.sh
file (must be ignored by VCS):gcloud kms encrypt \ --project=professionalserviceslabs \ --location=global \ --keyring=ps-keyring \ --key=commercetools-paypal-plus-integration \ --plaintext-file=build-settings.sh \ --ciphertext-file=build-settings.sh.enc
-
Decrypt
build-settings.sh.enc
:gcloud kms decrypt \ --project=professionalserviceslabs \ --location=global \ --keyring=ps-keyring \ --key=commercetools-paypal-plus-integration \ --plaintext-file=build-settings.sh \ --ciphertext-file=build-settings.sh.enc
-
-
This module is deployed as docker image to dockerhub.
-
The build and deployment of the docker are done using github actions.
-
On each push to the remote github repository, the github action ci is triggered, which builds the project and executes its tests.
-
The github action cd is used to create the docker-image and deploy it to dockerhub. This action is triggered when a git release tag is created.
There are two ways to create the release-tag:
- via command line
git tag -a v1.0.1 -m "Minor text adjustments."
- via Github UI