This site requires javascript to be enabled.

Visa

Results for

Results for Searching

Integration

Prerequisites

To follow along with this guide, please ensure that you have the following ready:

MyCheckout hosted payment pages

The fastest way to start accepting Card Payments is to make use of the MyCheckout hosted payment pages.

As we host the pages and handle the card data, you only need to be PCI SAQ-A compliant rather than any of the higher levels with more stringent requirements.

To start using the MyCheckout hosted payment pages, you only need to make a POST /v1/{merchantId}/hostedcheckouts API call with an order object containing a amountOfMoney object and a customer object. The customer object has a lot of options but only the billingAddress object with a countryCode property is required for a basic checkout.

This is how a minimum payload would look like:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    }
}

This will return a response like this:


{
   "RETURNMAC" : "33893c8a-1e6f-4e02-9015-9d4e34b08d41",
   "hostedCheckoutId" : "0634e88f-7255-71ff-860e-3f55f70978cc",
   "partialRedirectUrl" : "pay1.preprod.secured-by-ingenico.com/checkout/1010-e6e2e548a8024375a3c22ae4cea99f59:0634e88f-7255-71ff-860e-3f55f70978cc:80152ac992ea431ea1da027d1c3e6df4"
}

From the response, the important properties are hostedCheckoutId and partialRedirectUrl.

The hostedCheckoutId can be used in subsequent API calls such as GET /v1/{merchantId}/hostedcheckouts/{hostedCheckoutId} which will allow you to retrieve details regarding the transaction.

The partialRedirectUrl is used to create the URL where the customer needs to be redirected to. By default, we configure every account with the subdomain payment so you can always concatenate https://payment. with the partialRedirectUrl, which will result in URL such as:

https://payment.pay1.preprod.secured-by-ingenico.com/checkout/1010-e6e2e548a8024375a3c22ae4cea99f59:0634e88f-7255-71ff-860e-3f55f70978cc:80152ac992ea431ea1da027d1c3e6df4

More detailed information on how to customize the MyCheckout hosted payment pages (e.g. subdomains, design, languages etc.) can be found here.

At its core, this is all you need to start accepting Card Payments, more complex options will be covered later.

The hostedCheckoutId is only valid for 2 hours, please ensure to store the createdPaymentOutput.payment.id from the GET /v1/{merchantId}/hostedcheckouts/{hostedCheckoutId} response in order to be able to retrieve data after 2 hours have elapsed via the GET /v1/{merchantId}/payments/{paymentId} API call.
Alternatively, we can also optionally send a Webhooks event which will contain it. Please refer to this page for further information regarding Webhooks.

Server to Server

If you choose to not use the MyCheckout hosted payment pages option, perhaps that you require more styling customization options, or already have an existing payment page, you can build a direct integration using our SDKs (or integrate our API from scratch).

Within this option, you have variations depending on your PCI DSS compliance level.

Client Encryption

The Client API can be used for a variety of purposes, it allows you to perform actions such as collecting card data from your customers without needing to be PCI SAQ-D compliant, while also not requiring the usage of our checkout.

All you need is to be able to create a session via the POST /v1/{merchantId}/sessions API call. This will grant you access to the Client API.

It is highly recommended that you use our Client SDKs.

This guide only describes generally what steps you need to go through, the in-depth SDK pages describe how to perform the steps with SDK specific examples.

We provide these Client SDKs for:

These pages include detailed information on how to use the SDKs with language specific examples.

We also have references implementations showing how to use these SDK on GitHub.

If you choose to not use our Client SDKs, at the very least, please use the encryption methods we provide in these SDKs as it can be a bit tricky to do a custom implementation.

Since this is is not purely Cards related, additional details can be seen after expanding.

Expand Client Encryption Section
Basic Usage

To explain how this works, we will use card payments as example as it also demonstrates the PCI compliance aspects.

When you utilize the Client API, you will not handle data required for the various payment products directly; all data input by the customer (e.g. card data )will be securely encrypted on the client side.

In case of Card Payments, you need to be PCI SAQ-AEP compliant to use the Client API.

The general steps required to perform a Card Payment is as such:

  1. Create a session via our Server API
  2. Use the session to perform a GET /client/v1/{customerId}/products or GET /client/v1/{customerId}/products/{paymentProductId} API call to retrieve all the product information for the products configured for your Merchant ID.
  3. Use payment product information to determine what properties need to be collected from the customers and display those.
  4. Use the session to perform an POST /client/v1/{customerId}/services/getIINdetails to ensure that the card number that the customer input is supported by your Merchant ID.
  5. Use the session retrieve the publicKey
  6. Encrypt the data input by the customer
  7. Send the encrypted data to us to perform the payment

This process is similar for other non-card products, an example of what is different is that non-card products have no reason to verify IIN details.

Session Creation

Basic Session

To create a session perform a POST /v1/{merchantId}/sessions API call, optionally, a body can be provided to customize the session but it is not required. The call will result in a response such as this:


{
    "assetUrl": "https://assets.pay1.preprod.secured-by-ingenico.com/",
    "clientApiUrl": "https://ams1.preprod.api-ingenico.com/client",
    "clientSessionId": "0ec162d940ab4effa56ff6ee7fe999f6",
    "customerId": "1010-73478d14199b45c4a3c72fe41e40d6bc",
    "region": "EU"
}

The assetUrl is used to retrieve assets such as logos of payment products, for example, GET /client/v1/{customerId}/products/{paymentProductId} will return various details for a specific Payment Product and one of those is the URI for the logo which can be combined with the assetUrl for the final URL.

The clientApiUrl and customerId are used to construct the API endpoint for all Client API calls.

The clientSessionId will be used to construct the Authorization header for Client API calls.

You are now ready to perform Client API calls and can retrieve the data required to show the correct data input fields to the customer.

Session Customization

As mentioned earlier, you can customize the session if required, there are 2 options:

  • Product Filtering
  • Tokens

The product filtering option, enabled through the paymentProductFilters property, allows you to limit what payment products are valid for this session, and this can be done either through whitelisting or blacklisting.

This object can contain either the exclude or restrictTo properties, with the former being the blacklist option and the latter the whitelist option.

Both of these objects can contain either a groups array or a products array. There is currently only 1 valid value for the groups array which is cards (this includes every payment product labeled as payment method Cards (debit & credit) here). This allows you to either whitelist or blacklist a set of payment products at once without having to separately list all the payment product IDs. The products array will simply contain a list payment product IDs.

You can use it as such:


        "paymentProductFilters": {
            "restrictTo": {
                "products": [
                    1,
                    3
                ]
            }
        }

In the above example, only Visa and Mastercard would be valid for the session regardless of what other payment products are configured for the Merchant ID.

The tokens option, enabled through the tokens property, allows you to provide an array of previously stored tokens which will be eligible for the customer to use. For example, if a customer previously indicated that they wised to store their card data for future use and you created a token out of it, this is how you would present it as a valid option to use for payments to the customer.

You can use it as such:


    "tokens": [
        "b3b7c625-8561-47c0-9375-1fe9cd8f33f9",
        "e2ae0b6b-20d3-4193-9594-a1d6f5cb92d1",
        "7fdb13e4-b0c2-4550-8c64-906a5d96c5eb"
    ]
Product Fields

In order to know what information needs to be collected from the customer for any given payment product, you need to perform a GET /client/v1/{customerId}/products or GET /client/v1/{customerId}/products/{paymentProductId} API call, where the former gives you the details for every payment product configured for your account, and the latter for a specified payment product.

For example, if you were to request all details for Visa, you would get a response similar to this:

Expand example response

{
    "deviceFingerprintEnabled": true,
    "allowsInstallments": false,
    "allowsRecurring": true,
    "allowsTokenization": true,
    "authenticationIndicator": {
        "name": "AUTHENTICATIONINDICATOR",
        "value": "0"
    },
    "autoTokenized": false,
    "displayHints": {
        "displayOrder": 6,
        "label": "Visa",
        "logo": "templates/master/global/css/img/ppimages/pp_logo_1_v2.png"
    },
    "fields": [
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 19,
                        "minLength": 12
                    },
                    "luhn": {},
                    "regularExpression": {
                        "regularExpression": "^[0-9]{12,19}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": false,
                "displayOrder": 10,
                "formElement": {
                    "type": "text"
                },
                "label": "Card number",
                "mask": "{{9999}} {{9999}} {{9999}} {{9999}} {{999}}",
                "obfuscate": false,
                "placeholderLabel": "**** **** **** ****",
                "preferredInputType": "IntegerKeyboard"
            },
            "id": "cardNumber",
            "type": "numericstring"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "expirationDate": {},
                    "length": {
                        "maxLength": 4,
                        "minLength": 4
                    },
                    "regularExpression": {
                        "regularExpression": "^(?:0[1-9]|1[0-2])[0-9]{2}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": false,
                "displayOrder": 20,
                "formElement": {
                    "type": "text"
                },
                "label": "Expiry date",
                "mask": "{{99}}/{{99}}",
                "obfuscate": false,
                "placeholderLabel": "MM/YY",
                "preferredInputType": "IntegerKeyboard"
            },
            "id": "expiryDate",
            "type": "expirydate"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 4,
                        "minLength": 3
                    },
                    "regularExpression": {
                        "regularExpression": "^[0-9]{3,4}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": true,
                "displayOrder": 24,
                "formElement": {
                    "type": "text"
                },
                "label": "CVV",
                "mask": "{{9999}}",
                "obfuscate": false,
                "placeholderLabel": "123",
                "preferredInputType": "IntegerKeyboard",
                "tooltip": {
                    "image": "templates/master/global/css/img/ppimages/ppf_cvv_v1.png",
                    "label": "Please enter your security code as shown in the image"
                }
            },
            "id": "cvv",
            "type": "numericstring"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 50,
                        "minLength": 2
                    },
                    "regularExpression": {
                        "regularExpression": "^[-\\sA-Za-z0-9ªºÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƒƠơƯưǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǺǻǼǽǾǿȘșȚțΆΈΉΊΌΎΏΑΓΔΕΖΗΙΚΛΝΟΡΣΥΩΪΫάέήίαβγδεζηικλμνξοπρςστυψωϊϋόύώЁЄЇАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёєїҐґẠạẢảấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹÞþ.'/]+$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": false,
                "displayOrder": 1142,
                "formElement": {
                    "type": "text"
                },
                "label": "Cardholder name",
                "obfuscate": false,
                "placeholderLabel": "John Doe",
                "preferredInputType": "StringKeyboard"
            },
            "id": "cardholderName",
            "type": "string"
        }
    ],
    "id": 1,
    "maxAmount": 1000000,
    "mobileIntegrationLevel": "OPTIMISED_SUPPORT",
    "paymentMethod": "card",
    "paymentProductGroup": "cards",
    "usesRedirectionTo3rdParty": false
}



There is a wide variety of meta data returned for Visa which you can use to display the correct input forms to your customers; the fields array contains objects with details about what properties are required and how to handle them.

In the above example, you can see that the mandatory properties for Visa are cardNumber, expiryDate, cvv and cardholderName.

Selecting Tokens

As mentioned earlier, you can supply a list of tokens that will be valid for a given session. In this case the output of either the GET /client/v1/{customerId}/products or GET /client/v1/{customerId}/products/{paymentProductId} API call responses will have an extra accountsOnFile array included per payment product object.

Expand example response

{
    "deviceFingerprintEnabled": true,
    "accountsOnFile": [
        {
            "attributes": [
                {
                    "key": "alias",
                    "value": "************0035",
                    "status": "READ_ONLY"
                },
                {
                    "key": "cardholderName",
                    "value": "Foo Bar",
                    "status": "READ_ONLY"
                },
                {
                    "key": "cardNumber",
                    "value": "************0035",
                    "status": "READ_ONLY"
                },
                {
                    "key": "expiryDate",
                    "value": "1233",
                    "status": "CAN_WRITE"
                }
            ],
            "displayHints": {
                "labelTemplate": [
                    {
                        "attributeKey": "alias",
                        "mask": "{{9999}} {{9999}} {{9999}} {{9999}} {{999}}"
                    }
                ],
                "logo": "templates/master/global/css/img/ppimages/pp_logo_3_v3.png"
            },
            "id": 2,
            "paymentProductId": 3
        },
        {
            "attributes": [
                {
                    "key": "alias",
                    "value": "************5918",
                    "status": "READ_ONLY"
                },
                {
                    "key": "cardholderName",
                    "value": "Foo Bar",
                    "status": "READ_ONLY"
                },
                {
                    "key": "cardNumber",
                    "value": "************5918",
                    "status": "READ_ONLY"
                },
                {
                    "key": "expiryDate",
                    "value": "1233",
                    "status": "CAN_WRITE"
                }
            ],
            "displayHints": {
                "labelTemplate": [
                    {
                        "attributeKey": "alias",
                        "mask": "{{9999}} {{9999}} {{9999}} {{9999}} {{999}}"
                    }
                ],
                "logo": "templates/master/global/css/img/ppimages/pp_logo_3_v3.png"
            },
            "id": 0,
            "paymentProductId": 3
        }
    ],
    "allowsInstallments": false,
    "allowsRecurring": true,
    "allowsTokenization": true,
    "authenticationIndicator": {
        "name": "AUTHENTICATIONINDICATOR",
        "value": "1"
    },
    "autoTokenized": false,
    "displayHints": {
        "displayOrder": 9,
        "label": "MasterCard",
        "logo": "templates/master/global/css/img/ppimages/pp_logo_3_v3.png"
    },
    "fields": [
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 19,
                        "minLength": 12
                    },
                    "luhn": {},
                    "regularExpression": {
                        "regularExpression": "^[0-9]{12,19}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": false,
                "displayOrder": 10,
                "formElement": {
                    "type": "text"
                },
                "label": "Card number",
                "mask": "{{9999}} {{9999}} {{9999}} {{9999}} {{999}}",
                "obfuscate": false,
                "placeholderLabel": "**** **** **** ****",
                "preferredInputType": "IntegerKeyboard"
            },
            "id": "cardNumber",
            "type": "numericstring"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "expirationDate": {},
                    "length": {
                        "maxLength": 4,
                        "minLength": 4
                    },
                    "regularExpression": {
                        "regularExpression": "^(?:0[1-9]|1[0-2])[0-9]{2}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": false,
                "displayOrder": 20,
                "formElement": {
                    "type": "text"
                },
                "label": "Expiry date",
                "mask": "{{99}}/{{99}}",
                "obfuscate": false,
                "placeholderLabel": "MM/YY",
                "preferredInputType": "IntegerKeyboard"
            },
            "id": "expiryDate",
            "type": "expirydate"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 4,
                        "minLength": 3
                    },
                    "regularExpression": {
                        "regularExpression": "^[0-9]{3,4}$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": true,
                "displayOrder": 24,
                "formElement": {
                    "type": "text"
                },
                "label": "CVC2",
                "mask": "{{9999}}",
                "obfuscate": false,
                "placeholderLabel": "123",
                "preferredInputType": "IntegerKeyboard",
                "tooltip": {
                    "image": "templates/master/global/css/img/ppimages/ppf_cvv_{paymentProductId}_v2.png"
                }
            },
            "id": "cvv",
            "type": "numericstring"
        },
        {
            "dataRestrictions": {
                "isRequired": true,
                "validators": {
                    "length": {
                        "maxLength": 50,
                        "minLength": 2
                    },
                    "regularExpression": {
                        "regularExpression": "^[-\\sA-Za-z0-9ªºÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāĂ㥹ĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħĨĩĪīĬĭĮįİıIJijĴĵĶķĹĺĻļĽľĿŀŁłŃńŅņŇňʼnŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŲųŴŵŶŷŸŹźŻżŽžſƒƠơƯưǍǎǏǐǑǒǓǔǕǖǗǘǙǚǛǜǺǻǼǽǾǿȘșȚțΆΈΉΊΌΎΏΑΓΔΕΖΗΙΚΛΝΟΡΣΥΩΪΫάέήίαβγδεζηικλμνξοπρςστυψωϊϋόύώЁЄЇАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяёєїҐґẠạẢảấẦầẨẩẪẫẬậẮắẰằẲẳẴẵẶặẸẹẺẻẼẽẾếỀềỂểỄễỆệỈỉỊịỌọỎỏỐốỒồỔổỖỗỘộỚớỜờỞởỠỡỢợỤụỦủỨứỪừỬửỮữỰựỲỳỴỵỶỷỸỹÞþ.'/]+$"
                    }
                }
            },
            "displayHints": {
                "alwaysShow": true,
                "displayOrder": 1142,
                "formElement": {
                    "type": "text"
                },
                "label": "Cardholder name",
                "obfuscate": false,
                "placeholderLabel": "John Doe",
                "preferredInputType": "StringKeyboard"
            },
            "id": "cardholderName",
            "type": "string"
        }
    ],
    "id": 3,
    "maxAmount": 1000000,
    "mobileIntegrationLevel": "OPTIMISED_SUPPORT",
    "paymentMethod": "card",
    "paymentProductGroup": "cards",
    "usesRedirectionTo3rdParty": false
}



In the above example, all details for payment product ID 3 have been requested which includes information of 2 tokens.

The attributes array within the individual token objects will provide you information to display to the customer, and whether the fields are editable, and any key that matches a mandatory field need not be submitted (unless it has been edited) as it is part of the token.

Encrypted Payload

After the customer has provided all necessary data, you will be ready to go to the encryption step.

While you could perform the next step manually, we recommend you to use our Client SDKs and use the helper utilities to generate the encryptedCustomerInput which can be used to perform the payment itself.

Pass a payload with all the information that needs to be encrypted to the encryption utility to generate the encryptedCustomerInput.

The payload needs at least the paymentProductId, clientSessionId and nonce properties, with other properties depending on the product. The paymentValues array is what will contain key value pairs of the data required for any particular payment product, for example a card payment’s payload would look like this:


{
    "tokenize": false,
    "paymentProductId": 1,
    "clientSessionId": "52419758a7904941bb73e4dc639b72c7",
    "nonce": "80fc3a40-0670-4b73-b362-745b6f2a2626",
    "paymentValues": [
        {
            "key": "expiryDate",
            "value": "1233"
        },
        {
            "key": "cvv",
            "value": "123"
        },
        {
            "key": "cardholderName",
            "value": "Foo Bar"
        },
        {
            "key": "cardNumber",
            "value": "4111111111111111"
        }
    ]
}

Since card payments can be tokenized, there is an additional property to control that.

Similarly, if you would like to submit a payload that specifies a token to be used, that would look like this:


{
    "paymentProductId": 3,
    "accountOnFileId": 2,
    "clientSessionId": "52419758a7904941bb73e4dc639b72c7",
    "nonce": "80fc3a40-0670-4b73-b362-745b6f2a2626",
    "paymentValues": [
        {
            "key": "cvv",
            "value": "123"
        }
    ]
}

With the encryptedCustomerInput you can now perform a POST /v1/{merchantId}/payments API call with an order object containing a amountOfMoney object and a customer object. The customer object has a lot of options but only the billingAddress object with a countryCode property is required for a basic payment.

The minimum required payload should look like this:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "encryptedCustomerInput": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJraWQiOiJlYmU2Yzc1Yi1lMmNlLTRkOGEtYmUyMi01MmYwMDBkZTY2ZTMifQ.gSrfVdp24Rxfm7141YIoWlVkdXEzZylroY5ASIWiH7EjS_6ak6dpHq7n289IYM2NPrUfO3guIMmYzxbXumUFUzQJnjV4yPQlT8dvgtoSbLGSku4vYxlhWkGpuD0bK8_M1a4QRaQULd-q4N084VmYeOEECDTMwTS46UuDxJdceJ1gkBrTH0SLgFPPYxLVL1tZ0TFqThJs-suBPAuUbBlaI0k3lS8OFnlNit-ABe9PQ_GN9z23HwRwWRYb4F7FFBw6xmmJXWD8cfqHLMojEhQ0i1Tz-17KAURkjKK_SoTY1Fl37ktzStxmgJE66_gR2QVbLak2nsI7GTwuHnTfuWXApQ.Zu7RpXUjtnEUozZSyxiZGw.kdo1xB2aLdGT5Vi4En4fmT8Xvqsx1TiNScF0-caRPr6H1gb971cXT2rmMpEwbKmjTJ-Pp3olts9-ESjxwDEwYd4QirCb9OzWu7zr6JAoWy3rbVcMOBgtZ5QdaOkwVNAlYEWNyroBgmoQBECfUTRNzcfrwftSbYVygYWMNQwUaraJIywEPYHwy7F_PP3npV56k5aLQtKU4oDH6Xn11X5dIcfLeayE0aCFHF_AIufQFGVRTwSjfvz8agDPz3ztaK_3Mn1-Cyj0zBQN492xRDMTKUky68G0w4oFwWn8jcDathpt5-nsGuFrvpwzc4CrHiSkIaMFpZlISuYVvS9Q7n74SfYoiBd53B8x86AZzKAbvNmouIqY7xxlQi5opCziXNIsNyxC-AwmEIKE5nRiwxBh8SFxTNq9DZ9cGGpP96MQSV4.32T2d4BROJ82diyUy6eoOl__U0KSrCSQDbDZ07iXsg0"
}

The encryptedCustomerInput can only be used once; after we have decrypted it and performed a payment, you will receive a 1008 - INVALID_CONSUMER_INPUT error when attempting to use it again.

Raw Data

If you wish to handle the card data directly, you can elect to go for this option.

After you have securely collected the card data from the customer, you need to perform a POST /v1/{merchantId}/payments API call, similar to the last step in the Client Encryption option. The difference being that rather than submitting a encryptedCustomerInput property, you need to submit a cardPaymentMethodSpecificInput object.

The payload in this case requires the same order object as described in the last step of 3.1 Client Encryption, and a cardPaymentMethodSpecificInput object, this object needs to contain a paymentProductId property and a card object.

Generally speaking, the card object should contain cardNumber, expiryDate, cvv, and cardholderName properties for a normal one-off payment.

The cardholderName is often different from the name a customer uses when registering an account, the cardholderName refers to the official name the customer uses with their issuing bank.

This will result in a payload looking like this:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        }
    }
}

You need to be PCI SAQ-D compliant to use this option.

Additional Features

There are several additional features that can be used depending on your needs.

Authorization Types

Changing the authorization type changes the behavior for both the customer and for you as merchant.

From a customer’s perspective, what changes is how long the authorization is valid for, while it can differ a bit based on scheme and industry, generally the characteristics are:

  • Pre-authorizations are valid for ±30 days, you may capture an amount lower than authorized, and you can perform the capture at any time during the authorization period.
  • Final authorizations are valid for ±7 days, you must capture the exact amount authorized, and you can perform the capture at any time during the authorization period.
  • Sale authorizations are immediately captured

The optional property authorizationMode in the cardPaymentMethodSpecificInput object can be used to set the type of authorization you are sending. This applies to both the POST /v1/{merchantId}/hostedcheckouts and POST /v1/{merchantId}/payments API calls.

The valid values are FINAL_AUTHORIZATION, PRE_AUTHORIZATION, or SALE.

An example of how to set the property for the POST /v1/{merchantId}/payments API call:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "authorizationMode": "PRE_AUTHORIZATION",
        "paymentProductId": 1,
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        }
    }
}

However, as the note on the API References mentions, support for this feature varies a bit depending on which acquirer is configured for your Merchant ID. Due this, the recommended way of setting an authorization type is to request this to be set as a fixed setting on your Merchant ID by contacting us.

As mentioned earlier, a characteristic of the Final and Pre-authorizations (either through the API or via configuration) is that you can choose when to capture, however, in order to be able to do that, you need to submit another property, namely the requiresApproval property. If you do not set this, regardless of which authorization type you use, it will behave as the Sale authorization.

Just like the authorization type, you can also contact us to make this a fixed setting so that you do not need to send requiresApproval in the request.

If you do choose to send it in the request, it should look like this:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "authorizationMode": "PRE_AUTHORIZATION",
        "requiresApproval": true,
        "paymentProductId": 1,
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        }
    }
}

The response to POST /v1/{merchantId}/payments (it may look different based on your Merchant ID configuration) would look like:

Expand example response

{
    "creationOutput": {
        "additionalReference": "00000010101000007393",
        "externalReference": "000000101010000073930000100001"
    },
    "payment": {
        "id": "000000101010000073930000100001",
        "paymentOutput": {
            "amountOfMoney": {
                "amount": 100,
                "currencyCode": "SEK"
            },
            "references": {
                "paymentReference": "0",
                "providerId": "3500",
                "providerReference": "75242216"
            },
            "paymentMethod": "card",
            "cardPaymentMethodSpecificOutput": {
                "paymentProductId": 1,
                "authorisationCode": "HOSTOK",
                "fraudResults": {
                    "fraudServiceResult": "accepted",
                    "avsResult": "0",
                    "cvvResult": "0",
                    "microsoftFraudProtection": {
                        "fraudScore": 14
                    }
                },
                "card": {
                    "cardNumber": "411111******1111",
                    "expiryDate": "1233"
                }
            }
        },
        "status": "PENDING_APPROVAL",
        "statusOutput": {
            "isCancellable": true,
            "statusCategory": "PENDING_MERCHANT",
            "statusCode": 680,
            "statusCodeChangeDateTime": "20221021155348",
            "isAuthorized": true,
            "isRefundable": false
        }
    }
}

Similarly, GET /v1/{merchantId}/payments/{paymentId} will return a response like this:

Expand example response

{
    "id": "000000101010000073930000100001",
    "paymentOutput": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "references": {
            "paymentReference": "0",
            "providerId": "3500",
            "providerReference": "75242216"
        },
        "paymentMethod": "card",
        "cardPaymentMethodSpecificOutput": {
            "paymentProductId": 1,
            "authorisationCode": "HOSTOK",
            "fraudResults": {
                "fraudServiceResult": "accepted",
                "avsResult": "0",
                "cvvResult": "0",
                "microsoftFraudProtection": {
                    "fraudScore": 14
                }
            },
            "card": {
                "cardNumber": "411111******1111",
                "expiryDate": "1233"
            }
        }
    },
    "status": "PENDING_APPROVAL",
    "statusOutput": {
        "isCancellable": true,
        "statusCategory": "PENDING_MERCHANT",
        "statusCode": 680,
        "statusCodeChangeDateTime": "20221021155348",
        "isAuthorized": true,
        "isRefundable": false
    }
}

And here is how the equivalent GET /v1/{merchantId}/hostedcheckouts/{hostedCheckoutId}:

Expand example response

{
    "createdPaymentOutput": {
        "payment": {
            "id": "000000101010000073950000100001",
            "hostedCheckoutSpecificOutput": {
                "hostedCheckoutId": "06352a55-b0ea-71ff-8ee1-74df8590db6f"
            },
            "paymentOutput": {
                "amountOfMoney": {
                    "amount": 100,
                    "currencyCode": "SEK"
                },
                "references": {
                    "paymentReference": "0",
                    "providerId": "3500",
                    "providerReference": "77307321"
                },
                "paymentMethod": "card",
                "cardPaymentMethodSpecificOutput": {
                    "paymentProductId": 1,
                    "authorisationCode": "HOSTOK",
                    "fraudResults": {
                        "fraudServiceResult": "accepted",
                        "avsResult": "X",
                        "cvvResult": "M",
                        "microsoftFraudProtection": {
                            "deviceCountryCode": "nl",
                            "deviceId": "0a625c1803ce09c13e4d7d228efe7c3ae36243019a827ed7e2207aa765a3096c",
                            "fraudScore": 5,
                            "trueIpAddress": "85.115.33.180",
                            "userDeviceType": "browser_computer"
                        }
                    },
                    "card": {
                        "cardNumber": "411111******1111",
                        "expiryDate": "1230"
                    }
                }
            },
            "status": "PENDING_APPROVAL",
            "statusOutput": {
                "isCancellable": true,
                "statusCategory": "PENDING_MERCHANT",
                "statusCode": 680,
                "statusCodeChangeDateTime": "20221021155804",
                "isAuthorized": true,
                "isRefundable": false
            }
        },
        "paymentCreationReferences": {
            "additionalReference": "00000010101000007395",
            "externalReference": "000000101010000073950000100001"
        },
        "paymentStatusCategory": "SUCCESSFUL"
    },
    "status": "PAYMENT_CREATED"
}

As can be seen above, the status will be changed to PENDING_APPROVAL (as opposed to CAPTURE_REQUESTED which is the usual status of a successful card payment) which indicates that it is waiting for your approval to continue.

In order to instruct us to perform the capture, you need to send the approval with the POST /v1/{merchantId}/payments/{paymentId}/approve API call.

In case of a Pre-authorization, this API call is also used to modify the amount to be captured, in order to set an amount, simply submit the amount property with any value lower than the original authorization amount.

The payload should look like this:


{
    "amount": 50
}

After we receive your approval to capture, we will proceed with the transaction after which the status will change to CAPTURE_REQUESTED.

Credential on File

The Credential on File (also known as COF) framework can be a bit complicated to use due to differences between different schemes, but in short, this enables you to offer customer’s to store their card details for future use. Depending on PCI compliance level, you can either let us store the card details, or store them yourself.

We will focus on 2 major use-cases:

  1. Recurring
  2. Unscheduled Credential on File (UCOF)

Recurring is used when you need to do fixed cycle subscription payments (e.g. your monthly music streaming service). UCOF can be further split up into 2 use-cases; the first is when you offer your customers to save the card details so that on a future order, the customer can just select it from a list, the second is when you need to charge the customer on an irregular schedule such as an automatic top-up service where you need add balance when the customer’s balance hits a certain threshold.

Let us start with Recurring, to initiate a new recurring sequence you need to submit a few extra properties in cardPaymentMethodSpecificInput. By setting the isRecurring property to true, the transaction will be considered Recurring. The characteristics will be provided in the recurring object and this will contain the endDate, minFrequency and recurringPaymentSequenceIndicator.

Do note that in contrast to many other features described here, this does not work the same way for the POST /v1/{merchantId}/hostedcheckouts and POST /v1/{merchantId}/payments API calls.

The difference is that the isRecurring property will need to be submitted in the hostedCheckoutSpecificInput object instead of the cardPaymentMethodSpecificInput.

To give an example of the difference, this is the example payload for the POST /v1/{merchantId}/hostedcheckouts API call:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "recurring": {
            "endDate": 20301231,
            "minFrequency": 30,
            "recurringPaymentSequenceIndicator": "first"
        }
    },
    "hostedCheckoutSpecificInput": {
        "isRecurring": true
    }
}

And this is how it would look like for the POST /v1/{merchantId}/payments API call:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        },
        "isRecurring": true,
        "recurring": {
            "endDate": 20301231,
            "minFrequency": 30,
            "recurringPaymentSequenceIndicator": "first"
        }
    }
}

Here is an example of the output of GET /v1/{merchantId}/hostedcheckouts/{hostedCheckoutId} (it may look different based on your Merchant ID configuration):

Expand example response

{
    "createdPaymentOutput": {
        "payment": {
            "id": "000000101010000074310000100001",
            "hostedCheckoutSpecificOutput": {
                "hostedCheckoutId": "06359243-66db-71ff-8f99-f21b5dcf85c3"
            },
            "paymentOutput": {
                "amountOfMoney": {
                    "amount": 100,
                    "currencyCode": "SEK"
                },
                "references": {
                    "paymentReference": "0",
                    "providerId": "3500",
                    "providerReference": "96375930"
                },
                "paymentMethod": "card",
                "cardPaymentMethodSpecificOutput": {
                    "paymentProductId": 1,
                    "authorisationCode": "HOSTOK",
                    "fraudResults": {
                        "fraudServiceResult": "accepted",
                        "avsResult": "0",
                        "cvvResult": "0",
                        "microsoftFraudProtection": {
                            "fraudScore": 7
                        }
                    },
                    "card": {
                        "cardNumber": "411111******1111",
                        "expiryDate": "1230"
                    },
                    "schemeTransactionId": "000000000004034"
                }
            },
            "status": "PENDING_APPROVAL",
            "statusOutput": {
                "isCancellable": true,
                "statusCategory": "PENDING_MERCHANT",
                "statusCode": 600,
                "statusCodeChangeDateTime": "20221026141451",
                "isAuthorized": true,
                "isRefundable": false
            }
        },
        "paymentCreationReferences": {
            "additionalReference": "01010100000743100001",
            "externalReference": "0101010000074310000100001"
        },
        "paymentStatusCategory": "SUCCESSFUL",
        "tokenizationSucceeded": true,
        "tokens": "dab1549d-cbb1-4044-968a-b1c520eb8525"
    },
    "status": "PAYMENT_CREATED"
}

Similarly, GET /v1/{merchantId}/payments/{paymentId} will return a response like this:

Expand example response

{
    "id": "000000101010000074310000100001",
    "hostedCheckoutSpecificOutput": {
        "hostedCheckoutId": "06359243-66db-71ff-8f99-f21b5dcf85c3"
    },
    "paymentOutput": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "references": {
            "paymentReference": "0",
            "providerId": "3500",
            "providerReference": "96375930"
        },
        "paymentMethod": "card",
        "cardPaymentMethodSpecificOutput": {
            "paymentProductId": 1,
            "authorisationCode": "HOSTOK",
            "fraudResults": {
                "fraudServiceResult": "accepted",
                "avsResult": "0",
                "cvvResult": "0",
                "microsoftFraudProtection": {
                    "fraudScore": 7
                }
            },
            "card": {
                "cardNumber": "411111******1111",
                "expiryDate": "1230"
            },
            "schemeTransactionId": "000000000004034"
        }
    },
    "status": "PENDING_APPROVAL",
    "statusOutput": {
        "isCancellable": true,
        "statusCategory": "PENDING_MERCHANT",
        "statusCode": 600,
        "statusCodeChangeDateTime": "20221026141451",
        "isAuthorized": true,
        "isRefundable": false
    }
}

The response to POST /v1/{merchantId}/payments will look like this:

Expand example response

{
    "creationOutput": {
        "additionalReference": "01010100000743200001",
        "externalReference": "0101010000074320000100001",
        "isNewToken": false,
        "token": "dab1549d-cbb1-4044-968a-b1c520eb8525",
        "tokenizationSucceeded": true
    },
    "payment": {
        "id": "000000101010000074320000100001",
        "paymentOutput": {
            "amountOfMoney": {
                "amount": 100,
                "currencyCode": "SEK"
            },
            "references": {
                "paymentReference": "0",
                "providerId": "3500",
                "providerReference": "77894183"
            },
            "paymentMethod": "card",
            "cardPaymentMethodSpecificOutput": {
                "paymentProductId": 1,
                "authorisationCode": "HOSTOK",
                "fraudResults": {
                    "fraudServiceResult": "accepted",
                    "avsResult": "0",
                    "cvvResult": "0",
                    "microsoftFraudProtection": {
                        "fraudScore": 14
                    }
                },
                "card": {
                    "cardNumber": "411111******1111",
                    "expiryDate": "1233"
                },
                "schemeTransactionId": "000000000004035"
            }
        },
        "status": "PENDING_APPROVAL",
        "statusOutput": {
            "isCancellable": true,
            "statusCategory": "PENDING_MERCHANT",
            "statusCode": 600,
            "statusCodeChangeDateTime": "20221026141714",
            "isAuthorized": true,
            "isRefundable": false
        }
    }
}

You need to store the schemeTransactionId value from the response to be able to correctly perform the subsequent payments; this value needs to be submitted in all future API calls that belong to this same sequence of recurring payments.

In case of recurring payments we will also return a token or tokens property, in case you are not PCI SAQ-D compliant, you will need to use the token for future payments as you cannot store the credentials yourself in this case, for further information regarding tokenization, please refer to this page.

In order to initiate a subsequent payment in the recurring sequence, you need to perform a POST /v1/{merchantId}/payments API call with the initialSchemeTransactionId property containing the value of the schemeTransactionId you previously stored, and either the token or the card data (excluding the Card Security Code since there is no customer to provide it) directly.

This is an example payload using a token:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "token": "dab1549d-cbb1-4044-968a-b1c520eb8525",
        "isRecurring": true,
        "recurring": {
            "recurringPaymentSequenceIndicator": "recurring"
        },
        "initialSchemeTransactionId": "000000000004035"
    }
}

And this is how it would look like with the card details:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "card": {
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        },
        "isRecurring": true,
        "recurring": {
            "recurringPaymentSequenceIndicator": "recurring"
        },
        "initialSchemeTransactionId": "000000000004059"
    }
}

Next is UCOF, similar to recurring, you need to perform a first transaction, which will give you a schemeTransactionId that you need to submit in future UCOF transactions.

In order to perform any UCOF transaction, you need to submit the unscheduledCardOnFileRequestor and unscheduledCardOnFileSequenceIndicator properties within the cardPaymentMethodSpecificInput object, and the merchantCustomerId in the customer object.

To indicate that it is the first payment, you need to set unscheduledCardOnFileRequestor to cardholderInitiated and unscheduledCardOnFileSequenceIndicator to first. The value of the merchantCustomerId corresponds to the identifier you use internally to identify a customer.

This is how a POST /v1/{merchantId}/payments payload would look like:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "merchantCustomerId": "10011011101000",
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        },
        "unscheduledCardOnFileRequestor": "cardholderInitiated",
        "unscheduledCardOnFileSequenceIndicator": "first"
    }
}

Different from Recurring, UCOF does not need to submit any properties under the hostedCheckoutSpecificInput in order to perform a first transaction for the POST /v1/{merchantId}/hostedcheckouts, example payload:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "merchantCustomerId": "10011011101000",
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "unscheduledCardOnFileRequestor": "cardholderInitiated",
        "unscheduledCardOnFileSequenceIndicator": "first"
    }
}

The response will be similar to that for an initial recurring payment in that you will get a schemeTransactionId:

Example of the output of GET /v1/{merchantId}/hostedcheckouts/{hostedCheckoutId} (it may look different based on your Merchant ID configuration):

Expand example response

{
    "createdPaymentOutput": {
        "payment": {
            "id": "000000101010000074590000100001",
            "hostedCheckoutSpecificOutput": {
                "hostedCheckoutId": "0635bc96-d153-71ff-a1ad-2a9ebe878140"
            },
            "paymentOutput": {
                "amountOfMoney": {
                    "amount": 100,
                    "currencyCode": "SEK"
                },
                "references": {
                    "paymentReference": "0",
                    "providerId": "3500",
                    "providerReference": "39944275"
                },
                "paymentMethod": "card",
                "cardPaymentMethodSpecificOutput": {
                    "paymentProductId": 1,
                    "authorisationCode": "HOSTOK",
                    "fraudResults": {
                        "fraudServiceResult": "accepted",
                        "avsResult": "0",
                        "cvvResult": "0",
                        "microsoftFraudProtection": {
                            "deviceCountryCode": "nl",
                            "deviceId": "0a625c1803ce09c13e4d7d228efe7c3ae36243019a827ed7e2207aa765a3096c",
                            "fraudScore": 6,
                            "trueIpAddress": "85.115.33.180",
                            "userDeviceType": "browser_computer"
                        }
                    },
                    "card": {
                        "cardNumber": "411111******1111",
                        "expiryDate": "1233"
                    },
                    "schemeTransactionId": "000000000004479"
                }
            },
            "status": "PENDING_APPROVAL",
            "statusOutput": {
                "isCancellable": true,
                "statusCategory": "PENDING_MERCHANT",
                "statusCode": 600,
                "statusCodeChangeDateTime": "20221028142228",
                "isAuthorized": true,
                "isRefundable": false
            }
        },
        "paymentCreationReferences": {
            "additionalReference": "00000010101000007459",
            "externalReference": "000000101010000074590000100001"
        },
        "paymentStatusCategory": "SUCCESSFUL",
        "tokenizationSucceeded": true,
        "tokens": "dab1549d-cbb1-4044-968a-b1c520eb8525"
    },
    "status": "PAYMENT_CREATED"
}

Similarly, GET /v1/{merchantId}/payments/{paymentId} will return a response like this:

Expand example response

{
    "id": "000000101010000074590000100001",
    "hostedCheckoutSpecificOutput": {
        "hostedCheckoutId": "0635bc96-d153-71ff-a1ad-2a9ebe878140"
    },
    "paymentOutput": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "references": {
            "paymentReference": "0",
            "providerId": "3500",
            "providerReference": "39944275"
        },
        "paymentMethod": "card",
        "cardPaymentMethodSpecificOutput": {
            "paymentProductId": 1,
            "authorisationCode": "HOSTOK",
            "fraudResults": {
                "fraudServiceResult": "accepted",
                "avsResult": "0",
                "cvvResult": "0",
                "microsoftFraudProtection": {
                    "deviceCountryCode": "nl",
                    "deviceId": "0a625c1803ce09c13e4d7d228efe7c3ae36243019a827ed7e2207aa765a3096c",
                    "fraudScore": 6,
                    "trueIpAddress": "85.115.33.180",
                    "userDeviceType": "browser_computer"
                }
            },
            "card": {
                "cardNumber": "411111******1111",
                "expiryDate": "1233"
            },
            "schemeTransactionId": "000000000004479"
        }
    },
    "status": "PENDING_APPROVAL",
    "statusOutput": {
        "isCancellable": true,
        "statusCategory": "PENDING_MERCHANT",
        "statusCode": 600,
        "statusCodeChangeDateTime": "20221028142228",
        "isAuthorized": true,
        "isRefundable": false
    }
}

The response to POST /v1/{merchantId}/payments will look like this:

Expand example response

{
    "creationOutput": {
        "additionalReference": "00000010101000007460",
        "externalReference": "000000101010000074600000100001",
        "isNewToken": false,
        "token": "dab1549d-cbb1-4044-968a-b1c520eb8525",
        "tokenizationSucceeded": true
    },
    "payment": {
        "id": "000000101010000074600000100001",
        "paymentOutput": {
            "amountOfMoney": {
                "amount": 100,
                "currencyCode": "SEK"
            },
            "references": {
                "paymentReference": "0",
                "providerId": "3500",
                "providerReference": "21610305"
            },
            "paymentMethod": "card",
            "cardPaymentMethodSpecificOutput": {
                "paymentProductId": 1,
                "authorisationCode": "HOSTOK",
                "fraudResults": {
                    "fraudServiceResult": "accepted",
                    "avsResult": "0",
                    "cvvResult": "0",
                    "microsoftFraudProtection": {
                        "fraudScore": 19
                    }
                },
                "card": {
                    "cardNumber": "411111******1111",
                    "expiryDate": "1233"
                },
                "schemeTransactionId": "000000000004480"
            }
        },
        "status": "PENDING_APPROVAL",
        "statusOutput": {
            "isCancellable": true,
            "statusCategory": "PENDING_MERCHANT",
            "statusCode": 600,
            "statusCodeChangeDateTime": "20221028142315",
            "isAuthorized": true,
            "isRefundable": false
        }
    }
}

You now have the schemeTransactionId and the token (if you need it) for potential future transactions UCOF transactions.

You now have a few options, before that let us first go over the valid value combinations for unscheduledCardOnFileRequestor and unscheduledCardOnFileSequenceIndicator:

Requestor/indicator cardholderInitiated merchantInitiated
first ✔️
subsequent ✔️ ✔️

So in order to perform the first transaction, the combination first and cardholderInitiated were used, but that leaves the combinations subsequent and cardholderInitiated, and subsequent and merchantInitiated which cover 2 different use-cases as mentioned earlier.

When you send subsequent and cardholderInitiated, you do not need to submit the initialSchemeTransactionId property, but the customer must enter their Card Security Code. This option is used, for example, when the customer is going through the checkout on your website and they wish to use the card details that they had previously stored with you (or us if you use tokens). If you do use our tokens, you can submit a list of tokens under the hostedCheckoutSpecificInput object in the POST /v1/{merchantId}/hostedcheckouts API call to present a list of stored card details to customers:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "merchantCustomerId": "10011011101000",
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "unscheduledCardOnFileRequestor": "cardholderInitiated",
        "unscheduledCardOnFileSequenceIndicator": "subsequent"
    },
    "hostedCheckoutSpecificInput": {
        "tokens": "dab1549d-cbb1-4044-968a-b1c520eb8525, fe0a56f7-911c-430b-8147-358dded93a15"
    }
}

Which will be shown to the customer as such:

When you send subsequent and merchantInitiated, you do need to submit the initialSchemeTransactionId (with the value from the schemeTransactionId property from the first transaction), but the Card Security Code is not used. This option is used, for example, when you need to perform automatic balance top-ups without customer interaction.

Example payload for the POST /v1/{merchantId}/payments API call in case you store the card details:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "merchantCustomerId": "10011011101000",
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "card": {
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        },
        "unscheduledCardOnFileRequestor": "merchantInitiated",
        "unscheduledCardOnFileSequenceIndicator": "subsequent",
        "initialSchemeTransactionId": "000000000004480"
    }
}

And a version in case we store the card details:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "merchantCustomerId": "10011011101000",
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "token": "dab1549d-cbb1-4044-968a-b1c520eb8525",
        "unscheduledCardOnFileRequestor": "merchantInitiated",
        "unscheduledCardOnFileSequenceIndicator": "subsequent",
        "initialSchemeTransactionId": "000000000004480"
    }
}

You need to submit all the details necessary for 3D Secure the initial transaction for both Recurring and UCOF to ensure that 3D Secure v2 can be performed if necessary.

Mail Order/Telephone Order

In case you wish to accept Card Payments through Mail Order/Telephone (also known as MOTO), you need to ensure that you adjust the transactionChannel.

The transactionChannel can be send within the cardPaymentMethodSpecificInput object. This applies to both the POST /v1/{merchantId}/hostedcheckouts and POST /v1/{merchantId}/payments API calls.

The valid values are ECOMMERCE, MAIL, MOTO, or TELEPHONE where the default is set to ECOMMERCE when the property is not submitted.

MAIL and TELEPHONE are the preferred values but if you end up in a situation where it could be either then MOTO is fine to use.

The transaction channel is not returned in the response so you only need to take care of the request, here is an example of the payload:


{
    "order": {
        "amountOfMoney": {
            "amount": 100,
            "currencyCode": "SEK"
        },
        "customer": {
            "billingAddress": {
                "countryCode": "NL"
            }
        }
    },
    "cardPaymentMethodSpecificInput": {
        "paymentProductId": 1,
        "transactionChannel": "MOTO",
        "card": {
            "cvv": "123",
            "cardNumber": "4111111111111111",
            "expiryDate": "1233",
            "cardholderName": "Foo Bar"
        }
    }
}

As an alternative, we also offer a service that can be used to facilitate this called LinkPlus, this will provide a card details input screen and handle the transactionChannel setting.

Refunds and Reversals

If you wish to cancel a payment, you have 2 opportunities to do so; before the capture and after the capture, depending on the situation, you need to send a separate API call.

The first one we will cover is POST /v1/{merchantId}/payments/{paymentId}/cancel, cancelling a transaction refers reversing an authorization hold on the card.

This can be performed in the following situations:

  • The status is PENDING_APPROVAL and statusOutput.isCancellable is true
  • The status is CAPTURE_REQUESTED and statusOutput.isCancellable is true

Depending on which acquirer your Merchant ID has been configured with, the customer might not see an immediate release of the authorization hold if the configured acquirer does not support this feature.

If you set the requiresApproval property to true (as mentioned in the Credential on File section), there is also a way to undo the approval. In case you mistakenly called POST /v1/{merchantId}/payments/{paymentId}/approve, you may use the POST /v1/{merchantId}/payments/{paymentId}/cancelapproval API call to revert the transaction back to the PENDING_APPROVAL status.

Next is the POST /v1/{merchantId}/payments/{paymentId}/refund API call, this is only possible in 1 situation; if status is CAPTURE_REQUESTED and statusOutput.isCancellable is false.

You may refund any amount equal or lower to the captured amount, and may do this up till the total captured amount has been refunded.

A basic refund payload only requires the amountOfMoney object specifying the amount and currencyCode:


{
    "amountOfMoney": {
        "amount": 25,
        "currencyCode": "SEK"
    }
}

If you wish to see the details of your refund, you may use the GET /v1/{merchantId}/refunds/{refundId} API call.

Similar to payments, refunds also have an approval mechanism, the difference is that for refunds this is a fixed setting whereas payments had the option to send the requiresApproval property. If you have requested us to configure your Merchant ID with this setting for refunds, you can use the POST /v1/{merchantId}/refunds/{refundId}/approve API call in a similar fashion as the one for payments.

Refunds can also be cancelled, the conditions are the same as for payments, but instead of CAPTURE_REQUESTED, the status needs to be REFUND_REQUESTED instead. The usage of /v1/{merchantId}/refunds/{refundId}/cancel is similar to the payments equivalent.

Next Process flows