Card Payments

Take card payments through the API

To use this capability add ventrata/cardPayments to your X-Capabilities header.

This capability allows you to accept card payments via the API. It is possible to support multiple gateways, we document 3 in this spec: Stripe Worldpay and External

If you want us to add support for any other gateway please contact us at dev@ventrata.com

Stripe Gateway

We add a cardPayment key to the booking object, but if you're using the ventrata/cart capability then we add the same key to that as well. The value will look like this:

// ..rest of the booking or order object
"cardPayment": {
"gateway": "stripe",
"merchantName": "EdinExplore Tours",
"stripe": {
"version": "latest",
"paymentIntent": {
"id": "pi_1GG1qyCgIN4MRzgjHrJygury",
"publishableKey": "pk_test_f2FFE5An5Z0oMyxjlYgFRtdO",
"clientSecret": "pi_1GG1qyCgIN4MRzgjHrJygury_secret_PW4yD18qsNpU1Gz8etftPCZDM",
"amount": 9130,
"currency": "eur"
}
}
}

In the response we give some parameters under cardPayment which includes a stripe payment intent. You should follow the Stripe documentation from this section: https://stripe.com/docs/payments/accept-a-payment#web-collect-card-details

Check the Stripe documentation for more complete examples, but here is an example:

// Change this value cardPayment.stripe.publishableKey
var stripe = Stripe('pk_test_f2FFE5An5Z0oMyxjlYgFRtdO');
var elements = stripe.elements();
var card = elements.create("card");
card.mount("#card-element");
var form = document.getElementById('payment-form');
form.addEventListener('submit', function(ev) {
ev.preventDefault();
// Change this value cardPayment.stripe.clientSecret
stripe.confirmCardPayment("pi_1GG0bjCgIN4MRzgjMPBIM7SU_secret_t8LPS87iF0Kb1hU7qyCiR07xf", {
payment_method: {
card: card,
billing_details: {
name: 'Oliver Morgan'
}
}
}).then(function(result) {
if (result.error) {
// Show error to your customer (e.g., insufficient funds)
console.log(result.error.message);
} else {
// The payment has been processed!
if (result.paymentIntent.status === 'succeeded') {
// Send this payment intent back to Ventrata
}
}
});
});

Once the payment is confirmed you can perform either:

POST /bookings/:uuid/confirm POST /orders/:orderId/confirm

The second endpoint is if you're using the ventrata/cart capability. You then must include the payment intent id in the request body. If you prefer to use a Stripe payment method id instead that's also possible. Both examples are given below:

Payment Intent
Payment Method
Payment Intent
{
"cardPayment": {
"gateway": "stripe",
"amount": 9130,
"currency": "EUR",
"stripe": {
"paymentIntent": "pi_1GG0bjCgIN4MRzgjMPBIM7SU"
}
}
}
Payment Method
{
"cardPayment": {
"gateway": "stripe",
"amount": 9130,
"currency": "EUR",
"stripe": {
"paymentMethod": "pm_card_us"
}
}
}

Worldpay Gateway

We add a cardPayment key to the booking object, but if you're using the ventrata/cart capability then we add the same key to that as well. The value will look like this:

// ..rest of the booking or order object
"cardPayment": {
"gateway": "worldpay",
"merchantName": "EdinExplore Tours",
"worldpay": {
"cseKey": "DF7wTVmJJF7bAo67INBYBY-CBx-QuAAnPrzheh1vq3Y",
"googlePayMerchantId": "DEMO_123"
}
}

The response contains the gateway set to worldpay and under the worldpay object we provide you with the cseKey which is the client-side encryption key. For full documentation on how to integrate that into your web form check here https://developer.worldpay.com/docs/wpg/clientsideencryption/javascript-integration

For a quick example here you go:

// Set this to cardPayment.worldpay.cseKey
Worldpay.setPublicKey("DF7wTVmJJF7bAo67INBYBY-CBx-QuAAnPrzheh1vq3Y")
let card = {
cardHolderName: document.getElementbyid('worldpay_card_holder').value,
cardNumber: document.getElementbyid('worldpay_card_number').value,
cvc: document.getElementbyid('worldpay_card_cvc').value,
expiryMonth: document.getElementbyid('worldpay_card_month').value,
expiryYear: document.getElementbyid('worldpay_card_year').value
};
let token = Worldpay.encrypt(card, (err) => {
// Show error to your customer (e.g., insufficient funds)
});
if (token) {
// Success! Send this payment intent back to Ventrata
}

Once the payment is confirmed you can perform either:

POST /bookings/:uuid/confirm POST /orders/:orderId/confirm

The second endpoint is if you're using the ventrata/cart capability. You then must include the value of token in the cardPayment.worldpay.encryptedCseData field as shown in the example below:

{
"cardPayment": {
"gateway": "worldpay",
"amount": 9130,
"currency": "EUR",
"worldpay": {
"encryptedCseData": "7kxQGIUC9TJ158Uxnf3zJyDCfJrxstgDW7M0fu38fkOHJIJ2hMhE54tWHfejsS882cFb02_MKEM1wENDzttfnA"
}
}
}

Google Wallet / Pay

For Google Wallet / Pay payments please refer to the documentation here: https://developer.worldpay.com/docs/wpg/mobilewallets/googlepay

And you'll need to use the value of googlePayMerchantId given in the original gateway object. The Google Pay integration will return a JSON object with 3 parameters signature protocolVersion and signedMessage. To confirm the Google Pay payment you need to provide those 3 values for example:

{
"cardPayment": {
"gateway": "worldpay",
"amount": 9130,
"currency": "EUR",
"worldpay": {
"protocolVersion": "ECv1",
"signature": "TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ",
"signedMessage": "{\"encryptedMessage\":\"qrnk5KIV1syX8xuaJYpjAxjvTmAfH4arrxK+5wT4XNsn+K\/21QC5KyNyXVnaZsfz+FjYeZcZyTDsAXMGp01u9Psq3wp\/9aJJyZ9PS9+G2sVJ+symppSvIHhPYxfcFLyvDxcU7J2ZUIU2o7ZAsHkc9eUL0zqcjTfRxxyladBQmP3mjy7OtIZbXodSr5vZZqKrRm0MqmPo\/ISYor0gPmv+l+wRdLmCugdhBrg6mFGMuMBtnIjdtOZ+o4iSl4NqYo4BnI1py4GXG78FPHAlqU9Im+a9V6CtOsVG1eiLCn616fI8WorvPU46bDL7UApF\/+vRF6lJviV9Ie9pUJHejO5Z17GPTfBuxO8ZAPWqcPY\\u003d\",\"ephemeralPublicKey\":\"BJ8xDzqkngvLUZcOAkqgkYpwGIHi2TzOXO6ZDJpKHPJCnJZ5WZOTO0HtNrTiOgiVc2gdZQ+TWmcy2Y1KnNsAGpg\\u003d\",\"tag\":\"5t5pipJgyhxJUg9XcymYcdcKmtBxQ71VvF2uhnempbs\\u003d\"}"
}
}
}

External Gateway

The external gateway is the simplest and isn't actually a gateway at all. It allows you to register a virtual card payment which might have been taken on another gateway not supported by Ventrata.

We add a cardPayment key to the booking object, but if you're using the ventrata/cart capability then we add the same key to that as well. The value will look like this:

// ..rest of the booking or order object
"cardPayment": {
"gateway": "external",
"merchantName": "EdinExplore Tours"
}

Once the payment is confirmed you can perform either:

POST /bookings/:uuid/confirm POST /orders/:orderId/confirm

The second endpoint is if you're using the ventrata/cart capability. You can then optionally include notes in the external field which will can record an external payment reference which might be useful for backend reconciliation later. For example:

{
"cardPayment": {
"gateway": "external",
"amount": 9130,
"currency": "EUR",
"notes": "Ref#25172328"
}
}

The preferred way of getting gateway information is by first

get
Gateway

https://api.ventrata.com/octo/gateway
Get the default gateway for the account (NOT RECOMMENDED)
Request
Response
Request
Response
200: OK
{
"gateway": "worldpay",
"merchantName": "EdinExplore Tours",
"worldpay": {
"cseKey": "DF7wTVmJJF7bAo67INBYBY-CBx-QuAAnPrzheh1vq3Y",
"googlePayMerchantId": "DEMO_123"
}
}

The preferred way of getting the gateway configuration is on the booking/order object once its been reserved. That's because the gateway can vary depending on the products purchased or currency.

If you have a specific reason why you need the gateway configuration up-front and you have confirmed this is OK with the supplier and their configuration then you can call this endpoint and we'll return their default gateway configuration. The response if they were using worldpay will look like:

{
"gateway": "worldpay",
"merchantName": "EdinExplore Tours",
"worldpay": {
"cseKey": "DF7wTVmJJF7bAo67INBYBY-CBx-QuAAnPrzheh1vq3Y",
"googlePayMerchantId": "DEMO_123"
}
}

Examples of the other gateway types are shown above.