PayPal Complete Platform
This page provides an overview of the payments methods provided by the PayPal Complete Platform adapter in the IXOPAY platform. It also includes a full list of all configuration options available to you when integrating PayPal Complete Platform within your payments landscape, as well as an overview of the parameters required when submitting a transaction via IXOPAY's API.
Payment Methodsβ
| Payment Method | Transaction Flows | Transaction Types |
|---|---|---|
| PayPal | payment.js | Debit, Register, Preauthorize, Capture, Refund, Deregister, Recurring Debit/Preauthorize |
| PayPal ACDC | payment.js | Debit, Register, Preauthorize, Capture, Refund, Deregister, Recurring Debit/Preauthorize |
Integration guideβ
PayPalβ
PayPal requires you to display the PayPal Button on your Checkout page. This is handled by the payment.js integration.
β1. Embed the payment.min.js in your page
<script data-main="payment-js" src="https://gateway.ixopay.com/js/integrated/payment.1.3.min.js"></script>
It is important to set the data-main="payment-js" attribute on the script tag.
β2. Provide a DIV element on your page for the PayPal button
<div id='paypalButtonDiv'></div>
β3. Initialize the payment.js with the following parameters:
- ID of the DIV element
- Javascript function to handle payment completion
- Javascript function to handle error events
- Javascript function to handle cancellation events
- PayPal options (see below in example, refer to PayPal Documentation for possible options. The 'vault' option is per default set to 'false' if not otherwise specified. Please refer to the PayPal Documentation for the different button options)
- Your server endpoint where prepareCall is implemented. It could either be prepare-debit/prepare-preauthorize/prepare-register based on the transaction type
PayPal optionsβ
| Option | Required | Type | Description |
|---|---|---|---|
paypal-btn | yes | HTMLElement | Container element into which the button will be rendered. |
currency | yes | string | Currency |
intent | yes | string | Possible values - capture, authorize. Preauthorize flow requires to send authorize. Otherwise, it should be set to capture. |
createOrderEndpoint | yes | string | Your server endpoint where prepareX transaction is implemented |
vaultingWithoutPurchase | no | boolean | Only required with register flow. |
vault | no | boolean | Only required for register and debit/preauthorize with register = true |
userIdToken | no | boolean | Required for One-click payment |
enablePaylater | no | boolean | If you would like to display PayLater button, make sure that this field is set to true |
completeHandler | no | (data) => void | Triggered after successful payment. |
errorHandler | no | (error) => void | Triggered if an error occurred in payment process. |
cancelHandler | no | (data) => void | Triggered if the user canceled the payment process. |
button | no | object | https://developer.paypal.com/sdk/js/reference/#link-style |
sdk | no | object | Additional attributes for the PayPal SDK <script> tag, e.g. a CSP nonce. See Content Security Policy Nonce. |
fraudNetCmid | no | string | FraudNet client metadata id. Supplied for you on the hosted payment page; required if you enable FraudNet on your own checkout. See FraudNet. |
fraudNet | no | object | Optional FraudNet parameters. See FraudNet. |
<script>
var paymentJs = new PaymentJs('1.3');
errorHandler = function (error) {
// If an error prevents buyer checkout, alert the user that an error has occurred with the buttons using the onError callback
}
cancellationHandler = function (data) {
// When a buyer cancels a payment, they typically return to the parent page.
// You can instead use the onCancel function to show a cancellation page or return to the shopping cart.
}
completeHandler = function (paypalToken) {
// Captures the funds from the transaction and shows a message that tells the buyer the payment was successful.
// The method is called after the buyer approves the transaction on the paypal.com website.
// paypalToken should be passed to the debit request using extraData with the key 'paypalToken'
}
var paypalOptions = {
button: {
color: gold,
},
currency: "EUR",
intent: "capture" || "authorize", // debit = capture / preauthorize = authorize
vaultingWithoutPurchase: false, // should be set to true if you're doing register call
vault: false, // should be set to true if you would like to vault transaction using PayPal vaulting. for example register / debit + withRegister=true
locale: 'en_US', // override PayPals default language detection
fraudNetCmid: 'YOUR_32_CHAR_CMID', // only when FraudNet is enabled - see FraudNet
fraudNet: { // optional FraudNet tuning
mm: true,
},
};
// Prepare Transaction
var prepareCall = () => {
return fetch('{MY_PREPARE_ENDPOINT}', {
method: 'POST',
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID;
});
};
paymentJs.initPaypal(
'public-integration-key',
'paypal-button-div',
completeHandler,
errorHandler,
cancellationHandler,
paypalOptions,
prepareCall
);
</script>
β4. You can now call the Transaction API with the extraData.paypalToken set to the paypalToken returned to the completeHandler.
Prepare Transactionβ
PayPal requires to create order/setup token when the PayPal button is being pressed. That's being handled with prepare-debit/prepare-preauthorize/prepare-register API calls. Therefore, you would need to implement prepareDebit call and provide your server endpoint initializing PayPal via PaymentJs.
When using the Hosted Payment Page option, the prepare call can be handled automatically on our side. E.g.
prepareCall = function() {
return fetch('', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID;
});
}
Additional Prepare Transaction Parameters (CreateOrder)β
| Key | Values | Description |
|---|---|---|
| extraData.shipping_type | "SHIPPING", "PICKUP_IN_STORE", "PICKUP_FROM_PERSON" | Shipping type |
| extraData.shipping_cost | string | Shipping cost that will be displayed to the customer |
| extraData.shipping_preference | "GET_FROM_FILE", "NO_SHIPPING", "SET_PROVIDED_ADDRESS" | By default is being set on the connector level. However, by using this field can be overwritten per transaction. "SET_PROVIDED_ADDRESS" requires to provide shipping details |
| extraData.brand_name | string | Brand name |
| extraData.discount_amount | string | Discount amount |
| extraData.experience_context_shipping_preference | string | NO_SHIPPING = no shipping information, e.g. for digital goods SET_PROVIDED_ADDRESS = show address on PayPal but don't allow the customer to change it - item will be shipped to this address GET_FROM_FILE = shipping address was not provided, ask the buyer to select a shipping address from their PayPal profile |
| extraData.permit_multiple_payment_tokens | "true", "false" | Allows PayPal to create more than one payment token for the same PayPal account. By default this is set on the connector level. However, by using this field it can be overwritten per transaction. See Permit multiple payment tokens |
Permit multiple payment tokensβ
When vaulting a PayPal account, PayPal by default refuses to create a second payment token for a PayPal
account that is already vaulted with the same merchant, and returns the existing token instead. Setting
permit_multiple_payment_tokens to true allows PayPal to create an additional, independent payment token
for that same account.
The default is configured on the connector. To overwrite it for a single transaction, send
extraData.permit_multiple_payment_tokens with "true" or "false" β the transaction value always takes
precedence over the connector setting.
This applies to both vaulting flows:
| Flow | Where it is applied |
|---|---|
register (vaulting without purchase) | payment_source.paypal.permit_multiple_payment_tokens |
debit / preauthorize with withRegister = true | payment_source.paypal.attributes.vault.permit_multiple_payment_tokens |
This field only applies to the PayPal payment method. It is ignored for PayPal ACDC (card) transactions.
Example API Request:
{
"extraData": {
"paypalToken": "the token returned to your completeHandler",
"permit_multiple_payment_tokens": "true"
},
"withRegister": true
}
One-click payment / Customer Initiated Transactionβ
To render One-click payment button you would need to do a prepareDebit/preparePreauthorize call to retrieve user-token-id that needs to be passed to paymentJs via paypalOptions.userIdToken.
Example API Request:
{
"referenceUuid": "referenceUuid",
"extraData": {
"useReturnUserExperience": "true"
},
"transactionIndicator": "CARDONFILE"
}
| Key | Values | Description |
|---|---|---|
| referenceUuid | string | Initial transaction UUID |
| extraData.useReturnUserExperience | string | Expected value: true |
| transactionIndicator | CARDONFILE | Transaction Indicator should be CARDONFILE |
Example API Response:
{
"data": {
"success": true,
"returnType": "FINISHED",
"widgetToken": "eyJraWQiOiJhMDRiMjEwZDYzYjU0NWQzODEwOWUxNDM3ZjQ3ZWYxMiIsInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJpc3MiOiJodHRwczovL2FwaS5zYW3kYm94LasdasXBhbC5jb20iLCJzdWIiOiJDRVlINFVNUUpLMzM4IiwiYWNyIjpbImNsaWVudCJdLCJzY29wZSI6WyJCcmFpbnRyZWU6VmF1bHQiXSwib3B0aW9ucyI6eyJjdXN0b21lcl9pZCI6IklpUklSYWR1UEMifSwiYXoiOiJjY2cxOC5zbGMiLCJleHRlcm5hbF9pZCI6WyJQYXlQYWw6Q0VZSDRVTVFKSzMzOCIsIkJyYWludHJlZTozZG04NjdjdHJyZ3Y2Y2R3Il0sImV4cCI6MTcxNTcwNTEwNiwiaWF0IjoxNzE1NzA0MjA2LCJqdGkiOiJVMkFBSVA4NGprc3dfX1pYSHduR21KeVRQV003SkxzbFJLVlI2OHlxOEN4YmhHN1VWNkFWVVlyTTBqbXRyVEZmU3hBbjZKa0hlekNvSUtTdlZ6LUVpR1JxbVl4VWZaRHJOMDY1eXZQRkMySmgtQWkxaXNiV0xOWnh1MWJNa0VkdyJ9._s54YCC8MRqjllX2wDTmo4fQyTkUXl6_7aPOiNwBnVBJfLoQlwIZvtrkvgxU7RPBQy3uiRp-hhK4LGvwRC74CA"
}
}
Itemsβ
If submitting items, the following fields are Mandatory:
| Field |
|---|
| quantity |
| name |
| price |
| extraData.category |
Item Additional Parameters:
| Key | Values | Description |
|---|---|---|
| description | string | Item description |
| extraData.sku | string | Item SKU |
| l2l3Data.taxAmount | string | Item tax amount |
Additional Postback ExtraDataβ
| Key | Values | Description |
|---|---|---|
| seller_protection_status | string | Indicates whether the transaction is eligible for seller protection. For information, see PayPal Seller Protection for Merchants. |
| seller_protection_dispute_categories | array | An array of conditions that are covered for the transaction. |
| paypal_account_status | string | The account status indicates whether the buyer has verified the financial details associated with their PayPal account. |
PayPal ACDCβ
β1. Embed the payment.min.js in your page
<script data-main="payment-js" src="https://gateway.ixopay.com/js/integrated/payment.1.3.min.js"></script>
It is important to set the data-main="payment-js" attribute on the script tag.
β2. Provide an input field for the paypalToken which will be created later.
<input type="hidden" name="paypalToken" id="paypalToken" value="">
β3. Initialize the payment.js with the following parameters:
paymentJsPayPal.initPaypal(
'the public integration key of your connector',
{
cardName: 'card-name-field-container',
cardNumber: 'card-number-field-container',
cardCvv: 'card-cvv-field-container',
cardExpiry: 'card-expiry-field-container'
},
completeHandler, <!-- your callback function for when the token has been created -->
errorHandler, <!-- your callback function in case of error -->
cancellationHandler, <!-- your callback funciton in case of cancellation -->
payPalOptions, <!-- see PayPalOptions -->
prepareCall <!-- see prepareCall -->
);
completeHandler
The completeHandler callback will be called once the PayPal token has been created
Example
completeHandler = function (payPalToken) {
// do things when the PayPal token is created
console.log('done: ' + payPalToken);
// set the PayPal token and submit
<!-- important to name it paypalToken -->
document.getElementById("paypalToken").value = paypalToken;
document.getElementById("payment-form").submit();
}
prepareCall
The prepareCall must be implemented by you and is based on the transaction type: prepare-debit/prepare-preauthorize/prepare-register
Please refer to Prepare Transaction
When using the Hosted Payment Page option, the prepare call can be handled automatically on our side. E.g.
prepareCall = function() {
document.getElementById('loader').style.display = '';
return fetch('{{ paypal.prepareCallEndpoint }}', {
method: 'POST',
}).then(function (res) {
return res.json();
}).then(function (data) {
return data.orderID;
});
}
errorHandler, cancellationHandler
Here you may implement handling of scenarios for errors and user cancellations.
payPalOptions
| Option | Required | Type | Description |
|---|---|---|---|
| currency | yes | string | Currency |
| intent | yes | string | Possible values - capture, authorize. Preauthorize flow requires to send authorize. Otherwise, it should be set to capture. |
| vaultingWithoutPurchase | no | boolean | Should be set to true if you would like to vault transaction using PayPal vaulting. for example register / debit + withRegister=true |
| vault | no | boolean | Should be set to true if you would like to vault transaction using PayPal vaulting. for example register / debit + withRegister=true |
| style | no | object | Here you may override the style of the card fields |
| locale | no | string | Override PayPals default language detection |
| sdk | no | object | Additional attributes for the PayPal SDK <script> tag, e.g. a CSP nonce. |
Example
var paypalOptions = {
currency: "EUR",
intent: "authorize",
vaultingWithoutPurchase: false,
vault: false,
style: {
'input': {
'font-size': '12px',
'line-height': '1.7rem',
'color': '#000',
'padding': '5px',
'appearance': 'none'
},
':focus': {
'padding': '5px',
'outline': 'none'
}
}
};
β4. Trigger Payment with billing address data
Billing address data should be collected and submitted with the card data.
To trigger the payment you may use the paymentjs.trigger(data) function.
Example
document.getElementById('submit-btn').addEventListener('click', function (event) {
event.preventDefault();
paymentJs.trigger({
billingAddress: {
addressLine1: document.getElementById('card-billing-address-line-1').value,
addressLine2: document.getElementById('card-billing-address-line-2').value,
adminArea1: document.getElementById('card-billing-address-admin-area-line-1').value,
adminArea2: document.getElementById('card-billing-address-admin-area-line-2').value,
countryCode: document.getElementById('card-billing-address-country-code').value,
postalCode: document.getElementById('card-billing-address-postal-code').value
}
});
});
Please note that billing address line 1 and country code are mandatory.
Content Security Policy Nonceβ
If your checkout page is served with a strict Content-Security-Policy that does not allow 'unsafe-inline',
the PayPal SDK needs the nonce of your page to be able to inject its own inline scripts and styles.
PayPal reads that nonce from a data-csp-nonce attribute on its SDK script tag β see
PayPal: using a nonce instead of unsafe-inline.
Because the SDK script tag is created by payment.js and not by you, the nonce has to be handed over via the
sdk.dataAttributes option of paypalOptions. Every key/value pair given there is set as an attribute on the
SDK script tag, so csp-nonce ends up as data-csp-nonce.
| Option | Required | Type | Description |
|---|---|---|---|
sdk.dataAttributes | no | object | Key/value pairs that are set as attributes on the PayPal SDK script tag. Keys are automatically prefixed with data-, so csp-nonce and data-csp-nonce both work. |
Example
var paypalOptions = {
currency: "EUR",
intent: "capture",
sdk: {
dataAttributes: {
'csp-nonce': 'THE_NONCE_OF_YOUR_PAGE'
}
}
};
Set Transaction Contextβ
| Key | Paypal field: | Description | Type / Format |
|---|---|---|---|
| customer.identification | sender_account_id | Unique ID for the buyer's account on the merchant's platform | String, alphanumeric |
| customer.firstName | sender_first_name | Buyer's first name | String, alphanumeric |
| customer.lastName | sender_last_name | Buyer's last name | String, alphanumeric |
| customer.email | sender_email | Buyer's email address | String, email format |
| customer.billingPhone | sender_phone | Buyer's phone number | String, national notation |
| customer.billingCountry | sender_country_code | Country code of the buyer | String, ISO Alpha-2 |
| customer.extraData.sender_create_date | sender_create_date | Date the buyer's account was created | Date, ISO 8601 format |
| customer.extraData.cd_string_one | cd_string_one | Free text field | String, free form |
| customer.extraData. cd_string_two | cd_string_two | Free text field | String, free form |
| customer.extraData.cd_int_one | cd_int_one | Free number field | integer |
FraudNetβ
FraudNet is PayPal's browser-based device data collection. When enabled, the PayPal
connector JavaScript loads PayPal's FraudNet script on the checkout page, and a
client metadata id (CMID) is sent when the order is created, authorized and
captured, in the PayPal-Client-Metadata-Id header. PayPal correlates the two.
fraudNetSourceWebsiteId is yours to define. PayPal
recommends <MERCHANT_NAME>_<PAGE_NAME>, at most 32 characters, and requires a
different identifier for each page you integrate FraudNet on.
Connector configurationβ
| Setting | Required | Description |
|---|---|---|
enableFraudNet | no | Loads the FraudNet script and sends the CMID to PayPal. |
fraudNetSourceWebsiteId | when enableFraudNet is on | Source website identifier. You choose this value β PayPal does not assign it. Max 32 characters; use a different one per page, e.g. MERCHANTNAME_CHECKOUT. Without it nothing is loaded. |
Hosted Payment Pageβ
A new CMID is generated for you on every page load, as PayPal requires, and exposed to your payment template. Forward it into the PayPal options:
var paypalOptions = {
fraudNetCmid: "",
// ...your other options
};
Until your template is updated, FraudNet stays inactive β no script is loaded and no CMID is sent. Nothing else about the payment page changes.
Your own checkout (payment.js)β
Generate a 32-character alphanumeric CMID yourself β a new one each time the
checkout page is loaded β send it on the transaction as extraData.paypal_cmid,
and pass the same value to initPaypal:
paymentJs.initPaypal(
'public-integration-key',
'paypal-button-div',
completeHandler,
errorHandler,
cancellationHandler,
{ fraudNetCmid: yourCmid, /* ...your other options */ },
prepareCall
);
Both values must be identical. If they differ, PayPal receives device data it cannot associate with the transaction, and no error is reported.
Optional parametersβ
Pass any of these in the fraudNet option object:
| Field | Type | Default | Description |
|---|---|---|---|
mm | boolean | true | Collect mouse movement data. |
ts | object | β | Typing-speed monitoring. Takes type and fields, optionally min (default 8) and delegate (default false). |
fp | string | β | Your first-party cookie value, max 32 characters. |
cd | boolean | false | Cache the payload to prevent resubmission. |
bu | boolean | true | Enable the beacon. Setting this to false disables data transmission. |
Example:
var paypalOptions = {
fraudNetCmid: "",
fraudNet: {
mm: true,
ts: { type: 'CARD', fields: ['card-number'] },
},
};
Not supported: f, s and sandbox are set by us and cannot be overridden β
f must match the CMID sent in the API header, s comes from your connector
configuration, and sandbox follows the connector's test mode. b (custom beacon
endpoint) is not passed through. cb1 and cb2 are not supported, because the
FraudNet parameter block is JSON and cannot carry function values.
Timingβ
FraudNet is loaded when initPaypal is called. Call it on page load rather than
deferring it until the shopper selects PayPal, so FraudNet has the whole checkout
session to collect data.
Native appsβ
We do not provide a mobile SDK, so PayPal Magnes cannot be integrated on our
side. If your own app integrates Magnes, send the CMID it generates as
extraData.paypal_cmid and it is forwarded to PayPal. Values that are not 32
alphanumeric characters are ignored.
This requires enableFraudNet on the connector β it gates the header as well as
the browser snippet. You do not need fraudNetSourceWebsiteId for this, since no
page of ours is rendered; without the flag the CMID you send is dropped silently.
This applies to flows where we do not render the payment page. If your shopper is sent to our hosted payment page, that page generates its own CMID and overwrites the one you sent β FraudNet fires from our page, so a value you supplied would correspond to no collected data.
FraudNet is unrelated to the Fraud.net external risk check, which is configured as a risk rule rather than a connector setting.