Skip to main content

Payment Provider App Development Guide

Glossary

Payment Provider

Actually, short for Payment Service Provider, is any entity that provides all the necessary resources to allow a buyer to pay a merchant for purchased goods or hired services. These resources include information for the potential buyer about the available payment methods, installments, promotions, discounts, etc., as well as the technological resources to execute a payment.

Payment Method Type

The resource that allows a transfer of money from the consumer to the merchant. It can be any of, but not limited to:

  • Credit card
  • Debit card
  • Bank debit (aka "online debit")
  • Boleto (Brazil only)
  • PIX (Brazil only)
  • Ticket (aka "offline payment" or "payment voucher")
  • Wire transfer (between two bank accounts via traditional transfer)
  • Wallet (i.e. customer's account credit)
  • Cash
  • Others

Payment Method ID (formerly known as plain Payment Method)

For instance, if the Payment Method Type is credit card, then an example of Payment Method ID would be Visa, Mastercard or American Express. Some Payment Method Types, for example PIX, only have one Payment Method ID which happens to be PIX. Even though it's redundant, it leaves space for future variations.

Payment Option

The Payment Method is the resource that allows the transfer of money from the consumer to the merchant, while the Payment Option is how the Payment Method is used to execute the Transaction. In the physical world, if a credit card is selected as the Payment Method, then the payment could be executed using a card reader, or through an app that reads a QR code and processes the credit card information, etc.

A Payment Option has three main properties:

  • The Payment Provider that will execute the Transaction.
  • One or more supported Payment Method Types.
  • An Integration Type.

Integration Type

In terms of user experience, there might be different ways to integrate a Payment Option into a store. Some of them are:

  • Transparent: The whole process takes place in the store's website and all the relevant payment information are filled in a form rendered by the store, creating a seamless experience for the buyer.
  • External: At some point, the buyer is redirected to a website or app under the Payment Provider's domain where the buyer completes the payment process.
  • Modal: When the user submits our checkout form, a modal rendered by the Payment Provider is displayed, and the user completes the payment process within it.

Payment App

The development of a Nuvemshop Payment App, technologically speaking, more often than not, includes implementing our auth flow, REST APIs and webhooks, as well as the Payment Provider's authentication flow, backend-to-backend APIs and webhook/notifications.

Also, a Payment App includes a JavaScript implementation of our platform's checkout event handlers. This event handlers usually make requests to the app's backend which, in turn, makes requests to the Payment Provider's APIs.

App Installation and Payment Provider Creation Sequence

Introduction

This document provides an explanation of what a Payment App is and it will guide you through its development process.

In very few words, the steps to develop and deploy a Payment App at our platform are:

  1. The developer creates a partner account at the Nuvemshop Partner portal.
  2. The developer creates a Payment App at the Partner portal.
  3. The developer sets up a scalable backend on their own infrastructure where this app will run.
  4. The developer implements Nuvemshop's authorization flow.
  5. The developer implements Nuvemshop's REST APIs, including the Payment Provider and the Transaction resources.
  6. The developer implements their frontend scripts according to Nuvemshop’s JavaScript interface specifications, hosts the file in a public CDN and provides a link to the file through our API.
  7. The app is audited by Nuvemshop to test the implementation, scalability, stability and other important quality factors.
  8. The app is released by Nuvemshop.

Step 1: Partner Account and App Creation

To interact with our APIs, you must create a Payment App. This app represents a partner's product in our platform. Each app has a set of credentials required to authenticate against our platform and to be granted access to our APIs on behalf of the merchant, depending on the scopes enabled on the app's settings.

The steps to create a Payment App are as follows:

  1. If your company doesn't already have a Partner account, it can be created at our Partner Portal for Spanish LATAM or at our Partner Portal for Brazil.
  2. At the Partner portal, create an app and make sure that all the fields are completed with valid and real data (Note: fake data can be set while the app is in development mode).
  3. Take special care on the redirect URI which is a key part of the app installation process. You can modify it in the future, if necessary.
  4. You might want to include a good description of the payment services you offer, since our platform may bring new clients to your business.
  5. Make sure to choose the Payments category.
  6. Since you are creating a Payment App, make sure the app has the following scopes enabled on its configuration:
    • read_payments
    • write_payments
    • read_orders
    • write_orders

Important Note: Our Payments API is not enabled by default to all partner accounts and stores. Please, contact our Platform Development team to enable our Payments API on your partner account and its demo stores.

Step 2: App Installation and Payment Provider Creation Flow

A store can have n number of Payment Providers, and every store has their own Payment Providers with their own IDs and these are unrelated to the Payment Providers created by the same app on other stores. This means that each time a merchant installs your app, it must follow the Payment Provider creation flow for that app.

A Payment Provider object contain all the configuration to display payment methods, calculate prices, discounts, installments, among others, on the storefront and it also contains properties required for the checkout process.

The app installation flow is a standard OAuth 2 flow. The details on the app installation flow can be found here and the details on creating a Payment Provider can be found in our Payment Provider API Documentation.

During this flow, the app is expected to create the Payment Provider on the merchant's store.

The following sequence helps to illustrate the process concept:

  • Front: The web browser.
  • Nuvemshop IdP: Nuvemshop's Identity Provider.
  • Nuvemshop API: Self described.
  • Payment App: Any host on the Payment Provider side.

App Installation and Payment Provider Creation Sequence

Here's a less abstract example:

  • Front: The web browser.
  • Nuvemshop IdP: Nuvemshop's Identity Provider.
  • Nuvemshop API: Self described.
  • App's Backend: Place where both APIs, the one from Nuvemshop and the Payment Provider's one, live together.
  • Payment Provider API: The original Payment Provider API.

App Installation and Payment Provider Creation Sequence

Installing an App

(1) Get the Authorization Code

Use the following URL to start the app installation flow in a store:

https://<STORE_DOMAIN>.com/admin/apps/<APP_ID>/authorize

Note: Using placeholders for store_domain and app_id.

After accepting the application permissions, the merchant will be redirected to the redirect URI configured for the app, with the authorization code attached as a query string.

(2) Get the Access Token
curl --location --request POST 'https://www.tiendanube.com/apps/authorize/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<CLIENT_ID>' \
--data-urlencode 'client_secret=<CLIENT_SECRET>'' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'code=<AUTHORIZATION_CODE>'

Note 1: Using placeholders for client_id, client_secret and authorization_code.

Note 2: client_id and app_id represent the same value.

Payment Provider Configuration

You can find the list of Payment Provider object properties and their description in our Payment Provider API Documentation. All of the Payment Provider properties must have valid and real values (Note: fake data can be set while the app is in development mode).

Quick example:

curl --location --request POST 'https://api.tiendanube.com/v1/<STORE_ID>/payment_providers' \
--header 'Authentication: bearer <ACCESS_TOKEN>' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Acme Payments",
"description": "This is a brief description about the Acme Payment App.",

// Other Payment Provider properties.
}'

See a full example here.

We strongly suggest taking a close look at each of the Payment Provider's object properties to identify which ones may involve special handling and which ones may depend on the merchant's account configuration on the Payment Provider's site. Here's a quick overview of some of the ones we think need extra attention:

Checkout Configuration

Most of our APIs are based on REST interfaces. However, our Checkout API are based on both, REST and JavaScript interfaces. For the Checkout API, the configuration is made through our REST API but the frontend interactions are handled by the JavaScript interface. This allows the app developer to implement their own JavaScript SDKs on our checkout's frontend without any intervention from our development team.

The Payment Provider has two checkout related properties:

  • checkout_payment_options: List of Payment Options (such as transparent, external and modal), and their properties which include some like name, logo_url and supported_payment_method_types, among others.

  • checkout_js_url: The handlers for each checkout payment option (such as onLoad, onSubmit, etc.) are implemented through our JavaScript API. This property contains a HTTPS URL pointing to the file with these implementations so our checkout can run them on the frontend. This URL will be requested from the frontend, which means that the JS file must be hosted on a CDN capable of handling high traffic.

Note: The documentation of our Checkout API can be found here. Find more details about how to implement your own SDKs on the checkout JS file on the next section.

Supported Currencies

This property contains a list of supported currencies in ISO 4217 format.

The content of this field usually depends on settings on the merchant's app account. It is important to set valid and real values on this field making sure it reflects the merchants settings to avoid any unexpected behaviour.

Nuvemshop will use this value as a reference to show (allow) or hide (deny) the Payment Provider to buyers based on several business rules. Incorrect supplied values may result in incorrect or unsupported transactions being executed.

Storefront Scripts

Note that our frontend has two main components: Storefront and Checkout.

In case any frontend JS script unrelated to the checkout process like, for example, a fraud prevention script such as a digital footprint, needed to be added to the merchant website's storefront, our API has a resource for that purpose. Details on how to implement it can be found following this link.

Any request to the Script API should be sent during the Payment App's installation process. Your app will need an extra scope to access this resource: write_scripts. You must enable it at the app's configuration form on the Partner portal.

Step 3: Implementing the Checkout Flow

Nuvemshop provides all the necessary REST and JavaScript APIs to allow 3rd Party developers to take care of the whole implementation in a completely autonomous way, without intervention from our development team at all.

To illustrate the Payment App concept, let's say we have a Payment Provider called "Acme Payments". Here's a classic implementation diagram:

Classic Implementation Diagram

And here's the Nuvemshop App implementation diagram:

App Implementation Diagram

Available Checkout Payment Options

Currently, there are three main Checkout Payment Options. Below are quick descriptions of these to give some context and to align the reader with these concepts:

External Payment Option

Pretty much a standard redirect payment flow:

  1. The merchant's website sends the order data to the Payment Provider.
  2. The Payment Provider process the order, creates a payment preference and returns a redirect URL.
  3. The buyer is redirected to the Payment Provider's website.
  4. The buyer follows the checkout flow.
    • If the buyer has an account on the Payment Provider and wallet features are supported, this flow has a very smooth UX.
  5. The buyer is redirected back to the merchant's website and lands on a success, failure or cancel page.

Transparent Payment Option

This approach keeps the buyer at the merchant's website during all the checkout process:

  1. The form for each supported payment method is rendered on the merchant's website.
  2. The order data together with the selected payment method information is sent to the Payment Provider.
    • The credit or debit card sensible information never goes in a readable way to the Payment App's backend, but under a security strategy such as tokenization, encryption, etc., so that the Payment Provider will be the only able to read it's content.

This option gives the buyer a more store-branded experience which improves the user experience quality and it sense of security.

Some Payment Providers offer SDKs which render a lightbox or modal with an embedded iframe containing the Payment Provider's checkout UI on the merchant's website, giving the buyer a more transparent-like experience. In this case, though the buyer never really leaves the merchant's website, the checkout flow is run under the Payment Provider's domain.

This option allows the app to take full control of the frontend and render all the necessary elements to start the payment process.

Note: This implementation requires a rigorous internal certification process.

Checkout Payment Options Implementation (Checkout JS API)

As explained before, the developer is in charge of the JavaScript and REST implementations. The developer's JS implementation must follow our JS API specifications in order to properly integrate with our checkout. The file containing the app's JS implementation must be hosted on a CDN which must be able to handle potentially high traffic concurrency with, of course, a secure URL.

The app's checkout payment options script location must be indicated in the checkout_js_url property of the Payment Provider object, which our checkout will fetch when needed.

In this link you can find very detailed information on how the Checkout JS API is implemented.

Adding a Checkout Payment Option

As explained previously on the Checkout Configuration section, Checkout Payment Options have two interfaces: the REST interface for Payment Provider's configuration and the JavaScript interface for frontend event handling.

Our frontend provides a context through the LoadCheckoutPaymentContext() global function. This function takes another function as parameter which, in turn, takes two arguments, a Checkout object and an object of the PaymentOption family.

Therefore, your JavaScript file should look like this:

// acme.js
LoadCheckoutPaymentContext(function(Checkout, PaymentOptions) {
// Your code here.
});

To add a payment option, the method Checkout.addPaymentOption() must be called passing an instance of one of the available PaymentOptions objects. This instance must be created with all the necessary event handlers and an id property with the same value as the one configured in the associated checkout payment option at the Payment Provider object.

If, due to a business rule, the payment option should not be rendered, the Checkout.unLoadPaymentMethod() method must be called passing the payment option id.

A full description of the available PaymentOptions can be found here.

Transparent Card Implementation Example

Let's assume we've created a Payment App called "Acme Payments" and now we want to implement a Transparent Card Option so the user can pay with a credit or debit card inside the store checkout. So, we have to create an instance of PaymentOptions.Transparent.CardPayment and add it to the Checkout by using the Checkout.addPaymentOption() method:

// acme.js
LoadCheckoutPaymentContext(function(Checkout, PaymentOptions) {

// We create an instance of the transparent card payment option.

var AcmeTransparentCardPaymentOption = new PaymentOptions.Transparent.CardPayment({
id: "acme_transparent_card", // Same `id` as in the REST API checkout payment option.

// We indicate which extra fields we want to render in the form.
fields: {
card_holder_id_number: true
},

// `onLoad` handler is common to all `PaymentOtpions`.
onLoad: function() {
// Do something after the script loads.
},

// `onDataChange` handler is specific to `Transparent.CardPayment` option.
onDataChange: Checkout.utils.throttle(function() {
// It can be used to, for example, read the card data, perform validations, refresh the installments data, etc.
}, 700),

// `onSubmit` handler is common to all `PaymentOtpions`.
onSubmit: function() {
// Do something when user submits the payment.
},

// More handlers...
});

// Finally, we add the Payment Option to the Checkout object so it can be render according to the configuration set on the Payment Provider.

Checkout.addPaymentOption(AcmeTransparentCardPaymentOption);
});

PaymentOptions.Transparent.CardPayment takes care of rendering the card form and any information which may be relevant to the user, such as the available card brands and banks, and the installments data.

The fields property lets you select which optional input fields must be rendered on the card form. More information and a list of the available fields for this payment option can be found here.

The handler onDataChange will be invoked every time the checkout data is modified. This handler should implement business rules to validate the card's information and to update the installments data that is displayed to the buyer.

External Payment Implementation Example

Let's assume we've created a Payment App called "Acme Payments" and now we want to implement an External Payment Option so the user can paid from Acme's website. So, we have to create an instance of PaymentOptions.ExternalPayment and add it to the Checkout by using the Checkout.addPaymentOption() method:

// acme.js
LoadCheckoutPaymentContext(function(Checkout, PaymentOptions) {

// We create an instance of the external payment option.

var AcmeExternalPaymentOption = new PaymentOptions.ExternalPayment({
id: 'acme_external', // Same `id` as in the REST API checkout payment option.

// Define all necessary handlers here.
});

Checkout.addPaymentOption(AcmeExternalPaymentOption);
});

Available Information about the Ongoing Sale

The Checkout object provides the app with access to all the data related to the ongoing sale. We've got the following data groups:

  • Cart Information: Checkout.getData('order.cart').
  • Total cart price: Checkout.getData('totalPrice') (also indicated by Checkout.getData('order.cart.prices.total')).
  • ID of the store to which the cart belongs: Checkout.getData('storeId').
  • Customer Contact Information: Checkout.getData('order.contact').
  • Billing Information: Checkout.getData('order.billingAddress').
  • Shipping Information: Checkout.getData('order.shippingAddress').
  • Shipping Method Information: Checkout.getData('order.cart.shipping').
  • Payment Method Information: Checkout.getData('form').

Form Data

Some SDKs have mechanisms to render forms using field names as required. To protect the UI and provide the user with a clean and smooth user experience, all forms are rendered by our own code following our standards with custom field names, as explained above.

The object Checkout.getData('form') provides access to all the form fields. The payment method implementation must map each of the provided fields to the Payment Provider specific ones. In cases where a form with specific attributes needs to be submitted, we recommend using workarounds such as dynamically creating a hidden HTML form and submitting it using JavaScript.

Take into account that it is possible to request the consumer with more payment method information by rendering the optional fields on the form, depending on the selected payment method.

The available fields are:

Details on how to render the optional ones can be found here.

Adding Multiple Payment Options

Any number of payment options can be added to the checkout, combining external and transparent options as preferred. Nuvemshop's checkout may filter and hide some of them due to UX business rules being applied in order to improve the conversion rate.

For example, if "Acme Payments" wants to add three different payment options, one for their own external checkout, one for transparent card and another one for transparent boleto, the Payment Provider must specify the following checkout_payment_options:


{
"name": "Acme Payments",
"checkout_js_url": "https://cdn.acme.com/js/acme.js",

// Most of the Payment Provider properties are being omitted for better readability.

"checkout_payment_options": [
{
"id": "acme_external",
"name": "...",
"description": "...",
"logo_url": "...",
"supported_billing_countries": [
"BR"
],
"supported_payment_method_types": [
"credit_card",
"debit_card",
"boleto",
"pix",
"wire_transfer",
"wallet"
]
},
{
"id": "acme_transparent_card",
"name": "...",
"description": "...",
"logo_url": "...",
"supported_billing_countries": [
"BR"
],
"supported_payment_method_types": [
"credit_card",
"debit_card"
]
},
{
"id": "acme_transparent_boleto",
"name": "...",
"description": "...",
"logo_url": "...",
"supported_billing_countries": [
"BR"
],
"supported_payment_method_types": [
"boleto"
]
}
]
}

Then, the JavaScript implementation must have an instance for each Checkout Payment Option defined in the Payment Provider configuration:

LoadCheckoutPaymentContext(function(Checkout, PaymentOptions) {

let AcmeExternalPaymentOption = PaymentOptions.ExternalPayment({
id: 'acme_external',
fields: {...},
scripts: 'https://...',
onLoad: function(){...},
onSubmit: function(callback){...}
});

let AcmeTransparentCardPaymentOption = PaymentOptions.Transparent.CardPayment({
id: 'acme_transparent_card',
fields: {...},
scripts: 'https://...',
onLoad: function(){...},
onDataChange: function(){...},
onSubmit: function(callback){...}
});

let AcmeBoletoPaymentOption = PaymentOptions.Transparent.BoletoPayment({
id: 'acme_transparent_boleto',
fields: {...},
scripts: 'https://...',
onLoad: function(){...},
onSubmit: function(callback){...}
});

Checkout.add(AcmeExternalPaymentOption);
Checkout.add(AcmeTransparentCardPaymentOption);
Checkout.add(AcmeBoletoPaymentOption);
});

The script can have any number of checkout payment option instances, however, only those specified on the Payment Provider will be loaded. This allows the developer to have one static JavaScript file with logic for all configurations and then manage the available Payment Options for each store by adding them or not to the Payment Provider object.

Step 4: Transactions Implementation

Payment Apps work with orders. However, we don't provide any endpoint to directly change an order's payment status. That's where Transactions come in.

A Nuvemshop order can have many Transactions. A Transaction represents a single intention of moving money. If, for any reason (e.g. "insufficient funds") a Transaction failed, the next buyer's attempt to pay for the order would be a new Transaction. Likewise, an order paid with two credit cards would have one Transaction per credit card since two movements of money are being performed.

An order will automatically calculate its current payment status by checking all of its related Transactions and applying some business rules.

The full Transaction resource specification can be found here.

Creating a Transaction

The Payment App's backend must POST a Transaction to our platform as soon as one is created on the Payment Provider's side.

A Transaction not only helps to calculate an order payment status but also provides detailed information about the payment process so that the merchant can have full autonomy when handling orders and dealing with payment issues. Also, this information is always important to help the merchant with feature business decisions.

As a reference, here are two examples of the implementation of the POST Transaction request.

Example with transparent payment option:

Transparent Payment Option Sequence

Example with external payment option:

Redirect Payment Option Sequence

Transaction Properties

To ensure the best visibility on payments to both buyers and merchants,, it's very important to provide as much information as possible about each Transaction.

The TransactionInfo object should be completed with all the information required for each payment method.

Updating a Transaction

Nuvemshop's Transaction API is designed to reflect transactions that already exist and events related to those transactions that have already happened. Our Transaction API will automatically calculate the Transactions status on every TransactionEvent reported by the Payment Provider using the TransactionEvent endpoint.

Transaction Event workflows

The possible TransactionEvents that a Transaction can receive, depend on the possible workflows for each Payment Method Type.

The payment methods boleto, ticket, bank_debit, debit_card, wallet, wire_transfer, cash and pix, all share the same events workflow.

On the other hand, credit_card has its own workflow to support all the transaction events specific to this payment method type.

Order Management: Refunds and Chargebacks

Currently, consumers can't cancel orders from their purchase tracking page. In case they want to cancel a purchase, they will need to contact the seller.

Refunds

Merchants will be able to refund orders in two ways: from the store's dashboard or from the payment provider's dashboard.

If your Payment App supports Transaction refunds, then you must specify a refund URL within the Transaction data every time you create one. In this way, when merchants want to cancel an order from the store's dashboard, they will have the possibility of returning the money to the consumer. If this is so, Nuvemshop will notify your app of the refund intention through the refund URL specified for the Transaction related to the order. For more details on the implementation of the refund URL, visit the following link.

Likewise, if your app supports it, merchants could also perform Transaction refunds from the Payment Provider dashboard.

Either way, once a Transaction is refunded, then a TransactionEvent of type refund must be posted for the corresponding Nuvemshop's Transaction.

Chargebacks

Chargebacks are not supported at the moment, but we're looking forward to support them soon.

Infrastructure

It is important to take into account that Nuvemshop is a platform with 110k+ stores. This means the Payment App's backend will need to be able to handle high-traffic loads. All the necessary scaling, performance monitoring and alert triggering architecture must be implemented.

Special dates like Black Friday, Cyber Monday and Hot Sale should be handled with care. Your app should be able to sustain heavy loads with multiple very high traffic peaks.

App Metadata

The payments configuration dashboard of a store displays the available Payment Apps for that store so that the merchant can directly install them from there, instead of having to visit the App Store. Our Partner panel currently does not support to configure specific parameters of a payment application category, such as information about transaction costs (rates), supported payment methods, etc. However, our Support team will gladly set up that information for you.

The Payment App metadata information is country specific, so, if your app will be published in several countries, you'll need to provide a JSON for each country.

Note: All fields in the metadata object are optional. However, it is required to provide the merchant with a way to check the Payment App's rate information. Therefore, one of the rates_url or rates fields must be specified so that the app can be available to stores.

Properties

FieldTypeDescription
typeStringOne of aggregator, acquirer or gateway.
nameStringName to be displayed to merchants at the store admin tool.
descriptionStringBrief description about the app.
installation_descriptionStringBrief description of the app installation process.
installation_urlStringHTTPS URL of the Payment Provider installation site.
logo_urlsObjectObject containing key:value pair for each version of the logos for the frontend. Only supports HTTPS URLs. See Logos.
register_urlStringHTTPS URL of the Payment Provider registration site.
support_urlStringHTTPS URL of the Payment Provider support site.
support_phoneStringPayment Provider support phone number.
supported_payment_method_typesArray(String)List of available payment methods types. See Payment Method Types.
rates_urlString[Optional] HTTPS URL of the Payment Provider rate information site.
ratesArray(Object)[Optional] List of rates definitions for merchants by payment method type. See Rates.
featuresArray(String)[Optional] List of features offered by the Payment Provider. See Features.

Here is an example of what a JSON would look like for the metadata of a Payment App:

{
"type":"aggregator",
"name": "Acme Payments",
"description": "This is a brief description about the Acme Payment App.",
"logo_urls":{
"400x120":"https://cdn.acme.com/logos/400x120.png"
},
"installation_description":"This is a brief description of the installation process for the Acme Payment App.",
"register_url":"https://acme.com/register",
"support_phone":"+54123456789",
"supported_payment_method_types":[
"credit_card",
"wallet"
],
"features":[
"gateway",
"special_rates",
"transparent_checkout"
],
"rates_url":"https://acme.com/rates",
"rates":[
{
"payment_method_type":"credit_card",
"rates_definition":[
{
"percent_fee":"2.15",
"flat_fee":{
"currency":"ARS",
"value":"1.00"
},
"plus_tax":true,
"days_to_withdraw_money":14
}
]
},
{
"payment_method_type":"wallet",
"rates_definition":[
{
"percent_fee":"1.5",
"flat_fee":{
"currency":"ARS",
"value":"1.00"
},
"plus_tax":true,
"days_to_withdraw_money":7
}
]
}
]
}

Secure App Payment Processing Flow Beta

In order not to expose sensitive content of payment requests in your app's checkout JS script, such as merchant credentials, we provide a new secure payment flow, which requires some updates to your integration.

This new flow allows you to process the payment requests to your app's API in our backend, instead of executing them in the checkout frontend as it has been done up to now. To invoke the backend-to-backend payment processing from your app JS script you should use the new Checkout.processPayment method of our PaymentLib. To make this work, you must first provide us with some configurations so that our platform can execute the payment requests to your app's API as expected. In addition, you can optionally configure an authentication method for this request, having previously saved the credentials of each merchant in the the Payment Provider configuration related to the merchant's store.

In short, if you want to implement this new secure payment flow you must follow these steps:

  • Provide our Support team with the Payment Configurations of your app.
  • Implement the Checkout.processPayment and Checkout.showErrorCode methods in your app's JS script.
  • Save the authentication data for each store's Payment Provider (Optional).

Each of these steps will be explained in its corresponding section below.

Payment Configurations

The payment configurations are a series of directives that will allow us to build the payment request to your app's API in our backend in order to send it with the expected format and handle its response.

Properties

FieldTypeDescription
urlStringHTTPS URL of the app to send the POST request to create the payment.
headers Object[Optional] Object containing key-value pairs with the headers that must be added to the payment request. See Headers.
params Object[Optional] Object containing key-value pairs with the query parameters that must be appended to the end of the request URL.
payload Object[Optional] Object containing key-value pairs with the necessary mappings to adapt our standard payment payload to the payload required by the app payment request. See Payload.
errorCodePath String[Optional] Path to find the value of the error code or message in the payment request response in case of failure. See Error Mapping.
errorCodes Object[Optional] Object containing key-value pairs with the necessary mappings to adapt the error codes or messages returned by the payment request to standard values defined in our error code library. See Error Mapping.

Here is an example of what a JSON would look like for the configurations of a Payment App:

{
"url":"https://acme.com/payments",
"headers":{
"Connection":"keep-alive",
"PaymentOptionId":"#extra.option_id"
},
"params":{
"platform_id":1234,
"store_id":"#store_id"
},
"payload":{
"order_id":"#cartId",
"items":"#extra.items",
"payment":{
"amount":"#prices.total",
"currency":"#currency",
"installment":"#payment.installments",
"card_brand":"#extra.payment.card_brand"
},
"billing_address":"#billing_address",
"customer":"#extra.customer",
"card_token":"#extra.payment.card_token",
"country":"BR"
},
"errorCodePath":"errors.0.message",
"errorCodes":{
"The card does not have sufficient funds":"card_rejected_insufficient_funds",
"identification_value_is_empty":"consumer_id_invalid"
}
}

Headers

By default, we add the headers Content-Type: application/json and Accept: application/json to all payment requests. So it is not necessary to add them to the Headers configuration.

Payload

Our platform has a standard payment payload that contains data related to the order, which is generated when the consumer submits the checkout process after selecting a payment option of your app, but the format of this payload can be adapted to the needs of your app through the Payload configuration.

In case you want to customize the default payload, it is necessary to establish the mapping of the fields that you want to send in the final payment request to your app. If no mapping configuration is specified here, the payment request will be sent with the default payload generated by our platform.

Here is an example of what our standard payment payload looks like:

{
"store_id": 123456,
"cartId":1130553175,
"cartHash":"550dd9597106547ef8854f90a0de979c3331e4fd",
"prices":{
"shipping":"20.46",
"discount_gateway":0,
"discount_coupon":0,
"discount_promotion":0,
"discount_coupon_and_promotions":0,
"subtotal_with_promotions_applied":149.99,
"subtotal_with_promotions_and_coupon_applied":149.99,
"subtotal":149.99,
"total":170.45,
"total_usd":33.06949522555
},
"shipping_address":{
"id_number":null,
"zipcode":"18160000",
"first_name":"John",
"last_name":"Doe",
"address":"Rua Dejanira Soares Rosa",
"number":"21",
"floor":"Chacara",
"locality":"Parque Pirapora",
"city":"Salto de Pirapora",
"state":"São Paulo",
"country":"BR",
"phone":"+5515997772717",
"between_streets":null,
"reference":null
},
"billing_address":{
"id_number":"14910600850",
"zipcode":"18160000",
"first_name":"Marli",
"last_name":"JASMINEIRO FREITAS",
"address":"Rua Dejanira Soares Rosa",
"number":"21",
"floor":"Chacara",
"locality":"Parque Pirapora",
"city":"Salto de Pirapora",
"state":"São Paulo",
"country":"BR",
"phone":"+5515997772717"
},
"payment":{
"installments":"3",
"type":"credit_card",
"discount":0,
"category":"credit_card",
"providerId":"da78345e-b227-423a-bfa5-fc5d14b73d1d",
"option":"da78345e-b227-423a-bfa5-fc5d14b73d1d",
"extra":[]
},
"extra":{
// Extra data set by the app
}
}
Customize your Payment Payload

The Payload configuration is an object containing key-value pairs with all the necessary mappings to adapt our standard payment payload to the payload required by the app payment request.

The keys represent the names of the fields of the app's payment request body. Fields can be nested as many levels as needed. The idea is that the JSON object represented here has the same format as the JSON object that is required by the app's API.

The values that accompany each key can be fixed or variable. A variable value is distinguished from a fixed one because it begins with the symbol #. What follows this symbol is a path, which specifies the location of the source field in the standard payload where we must get the value to be placed in the destination field of the app's payload.

The # symbol in addition to the path content is what we call a marker. Defining a path inside a marker is pretty straightforward. The symbol . denotes a nesting level, that is, it will be used to access an object within the JSON. While to access the element of an array, you must indicate the number corresponding to the index to be accessed.

The following are some examples of markers that can be used to define config values:

  • For a field in the JSON root: #prices
  • For a nested field: #prices.subtotal
  • For an array element: #payment.extra.2.id (Here, we are accessing the third element of the payment->extra array and then getting its id value)

Based on the previous examples, if we want to include the value of the prices->total field of the standard payload in the payment->amount field of the app payment payload, we must add the following configuration:

{
"payload":{
"payment":{
"amount":"#prices.total"
}
}

To implement a fixed value in the app payment payload, you can simply add the following configuration. Note that values not containing the # symbol will not be translated and will be set with the content defined in the configuration:

{
"payload":{
"country":"BR"
}

Note: The markers notation explained above to define configuration values for the Payload also applies to the Headers, Parameters and Error Code Path configurations as well.

Checkout.processPayment and Checkout.showErrorCode methods

Checkout.processPayment(extraPayloadData) is a new method in our PaymentLib that allows your app to process backend-to-backend payments by calling it from the app's JS script. As we saw before, our platform has a generic payload that can be adapted to a specific payload that conforms to your app's API. But also, if necessary, this standard payload can be extended by adding custom data inside an extra object. The content of that extra object, which is optional, must be passed to the processPayment method. The extra content can also be translated following the same mapping rules as for the default payload.

Let's go with an example:

LoadCheckoutPaymentContext(function(Checkout, PaymentMethods) {
var External = PaymentMethods.ExternalPayment({
id: 'acme_redirect',
onSubmit: function(callback) {
let extraData = {
"email": Checkout.getData('order.contact.email')
}
Checkout.processPayment(extraData)
.then(function(payment) {
window.parent.location.href = payment.redirect_url;
})
.catch(function(error) {
Checkout.showErrorCode(error.response.data.message);
});
}
});
Checkout.addPaymentOption(External);
});

Now, in the Payload configuration we must indicate how to map the content of the extra object that we are adding to the default payload:

{
"payload":{
"customer":{
"email":"#extra.email"
}
}

Payment Request Responses

Success Response

In the case of a successful response from the app's API after making the payment request, the Checkout.processPayment method will return a response with the same content returned in the app's response (E.g. An object containing the data of the created payment).

Client Error Response

In the case of a 4xx error coming from the app's API, the Checkout.processPayment method will return an error response with the following format:

{
"status":422,
"message":"some_error_code"
}

Being the value of message one of the following:

  • Some code specified in our error codes library, if the error code or message returned by the app's API could be correctly translated. See Error Mapping.
  • payment_processing_error: A generic error from the Payment App, if the error code or message returned by the app's API could not be translated.
Error Mapping

Here's an example of how to configure the error mapping for your app's API errors to values from our standard error codes library. If your app's API error response already applies our standard error response format (E.g. {"error_code":"card_rejected"}), you don't need to implement any mapping here.

{
"errorCodePath":"errors.0.message",
"errorCodes":{
"The card does not have sufficient funds":"card_rejected_insufficient_funds",
"identification_value_is_empty":"consumer_id_invalid"
}
}
  • errorCodePath: Path to find the value of the error code or message in the payment request response in case of failure.
  • errorCodes: Object containing key-value pairs with the necessary mappings to adapt the error codes or messages returned by the payment request to standard values defined in our error code library.

Server Error Response

In the case of an non-controlled internal error, such as a fatal exception or a timeout, the Checkout.processPayment method will return a response with the generic error from Nuvemshop platform:

{
"status":500,
"message":"general_internal_error"
}

Payment Request Authentication

Optionally, you can also establish an authentication method to implement in the payment requests to your app's API. To do this, you must update the authentication field of the Payment Provider of each store with the values of the credentials corresponding to the merchant's account.

We currently support OAuth, Basic, Token, and API Key authentication methods. For more information on what to include in the authentication object, check the Authentication section of the Payment Provider documentation.

  • API Key: For this method we need to persist the api_key value.
    • This will add the following query param to the payment request: ?api_key=API_KEY_VALUE.
  • OAuth: For this method we need to persist the access_token, client_id, client_secret, refresh_token, refresh_token_url values and the expires_at date of the token.
    • This will add the following header to the payment request: Authorization: Bearer ACCESS_TOKEN_VALUE.
    • The merchant's access token will be periodically refreshed by our platform before its expiration date following the OAuth standard process and using the data provided here.
  • Basic Authentication: For this method we need to persist the username and password values.
    • This will add the following header to the payment request: Authorization: Basic ENCODED_STRING_VALUE, where ENCODED_STRING_VALUE is the Base64 encoding of username and password joined by a :.
  • Token: For this method we need to persist the access_token value.
    • This will add the following header to the payment request: Authorization: Bearer ACCESS_TOKEN_VALUE.
    • The difference with the OAuth method is that in this case the token is not refreshed on our part, but it can be updated by the app.

Payment Request Validation

To ensure the security of requests sent by us, the following measures were implemented:

Whitelisted IPs

Requests will only be sent from the following public IP addresses of our platform, which may change over time:

  • 100.24.139.24
  • 3.86.72.138
  • 54.88.85.16

Payload Signature

To further enhance security, each request will include the custom headers X-Signature and X-Timestamp.

X-Timestamp

Its purpose is to provide a timestamp for the request, indicating the exact time when the request was sent. This timestamp can be used to verify the timing of the request and to help prevent replay attacks.

Signing Process

The payload signature set in the X-Signature header is generated using the following process:

  • The payload is formatted as a JSON string, removing all line breaks.
  • The SHA-256 digest of the payload is calculated.
  • The URL, timestamp and digest are concatenated using pipes: [URL]|[Timestamp]|[Digest].
  • The concatenated string is signed using RSA encryption with SHA-256 algorithm and our private key.
Verification Process

The payload signature verification process on the app side must be as follows:

  • Format the received payload as a JSON string, removing all line breaks.
  • Calculate the SHA-256 digest of the payload.
  • Concatenate the URL, timestamp and digest using pipes: [URL]|[Timestamp]|[Digest].
  • Verify the concatenated string using RSA signature verification with SHA-256 algorithm, the received signature and the provided public key.

Note: Both the payload data and the result of the concatenated string must be encoded to UTF-8 for the verification process.

For more details, see the available implementations of the verification process described above: Node.js, PHP, Bash.

By implementing these security measures, we can ensure that only authorized requests are accepted and that the integrity of the payload is maintained throughout the transmission process.

FAQ

Why is my Payment App not being shown in the payment methods section of the store's admin panel

For a Payment App to be displayed in the store's admin panel, a series of requirements must be met:

  1. The app must be approved and published by Nuvemshop.
  2. The app should have the Payments category, and the write_payments permission. These options can be modified in the app details page on the partner's admin panel.
  3. The app needs to have it's metadata file inserted by Nuvemshop (see Metadata section above).

Note: Currently, only Nuvemshop can insert the app's Metadata. The process is as follows: the partner must create the Metadata JSON file, and then send it to the Platform Development team.

Why is my Payment App's option not being shown in the store's checkout?

For a Payment App option to be displayed in the checkout, a series of requirements must be met:

  1. All the requirements of the previous question.
  2. The app must be installed in the store.
  3. The app must have inserted a Payment Provider object.
  4. At least one of the ids of the Payment Options in the checkout.js file must match one of the ids of the checkout_payment_options in the Payment Provider object.