Rappi Payless Integration Guide

What is Rappi Payless?

Rappi Payless is a card-free payment method exclusive for our pickers (personal shoppers and couriers).

It enables point of sales systems (POSs) to process Rappi orders using payment codes generated from our pickers' mobile apps. Those payment codes are presented in the form of barcodes or QR codes.

Rappi Payless eliminates card transaction fees. It also simplifies conciliation and reporting, reduces checkout time, and eliminates operational issues related to releasing funds to debit cards.

Payless Flow

Click here do download Payless Flow document.

Take some time to analyze and understand all the possible scenarios the POS system will have to implement for a succesfull integration.

Integration Checklist

Upon successfully implementing Rappi Payless, your solution is expected to achieve the following:

  1. Ingest and process Rappi Payless payment codes either in the form of barcodes, QR-codes, or manually keyed-in.

  2. Send payment authorization requests to Rappi including all required details.

  3. Properly handle successful and declined transactions.

  4. Handle request timeouts with automatic retries.

  5. Switch to the fallback flow when communication with Rappi is unavailable.

  6. Daily conciliation of your transactions authorized by Rappi.

Introduction

This article provides an overview of Rappi Payless. Expect to learn more about:

  • our picker's experience with Rappi Payless,
  • the regular and fallback payment flows,
  • how to retrieve information to feed your conciliation process,
  • how to consume our sandbox methods to setup your test environment and run manual and automated tests on your integration.

Customer Places a New Order

1. Customer places a new order
Depending on the store configuration, an algorithm decides whether the order will be fulfilled by a personal shopper or a courier.
2. Picker goes through the shopping list
From the product list, the picker must mark each product as "found", "not found", or "replaced". Pickers can't advance to payment with pending products on the shopping list.
3. Picker arrives at the checkout
At this point, the picker app is in payment mode, generating new codes every 30 seconds.

Shopper Pays with Payless

4. (Optional) POS retrieve customer's identification
Useful when POS need user identification to apply loyalty markdown and promotions. Payment code is scanned from the picker's device to retrieve order details..
5. Cashier scans all products
The POS applies markdowns and promotions, and calculates the bill.
6. Payment code is scanned from the picker's device
The cashier has the option to scan a QR code, a barcode, or manually key-in a six-digit code. Learn more about the Rappi Payless payment code here. If the POS already has the barcode gathered before the scan of products when retrieving customer's identification, there's is no need to scan the barcode again.
7. POS sends the purchase details to Rappi
This is an HTTP request to our Payment Authorization endpoint. If successful, the POS will receive a transaction authorization token in response.
8. Picker takes a snapshot of the receipt
The order is ready for delivery.

The Rappi Payless Barcode

The Rappi Payless barcode (also referred to as payment code) is a random number associated with a unique order identifier in our database.

Barcodes are refreshed every 30 seconds on our pickers' apps payment views. They remain active for another 30 seconds when Rappi's backend finally expires them. Therefore, a barcode total time live (TTL) is one minute (60 seconds).

AUTO RETRIES ON TIMEOUTS

The barcode's TTL is 60 seconds. It's important to consider this information when implementing automatic retries in response to communication failures or timeouts between your POS and Rappi's backend.

Automatically Detect the Payment Method

We have created an optional configuration that allows retailers to specify a 2-letter prefix that will be prepended to each barcode we generate.

The common use for this configuration is having the POS automatically identify the payment method as Rappi Payless, without requiring the cashier to enter any commands from the keyboard.

Rappi Payless Fallback

Besides being used in the payment authorization request, the barcode's sequence of digits is also an input variable for the verification algorithm on the fallback flow. This is detailed in the section about communication failures.

Payment Authorization Requests

A successful transaction will always return an HTTP status code 200 and includes an authorization token in the response body. Both the barcode and the authorization token (or authorization code) for each successful transaction MUST be stored on the retailer's side for conciliation purposes.

Declined Authorizations

Although there are few reasons that lead a transaction to be declined by Rappi, those aren't yet defined by reason codes, and we don't expect the POS to expose this information. On each declined transaction, the picker receives a push notification with instructions to adjust the order before retrying the payment.

  1. Malformed payment authorization request

    There might be missing required properties or inconsistent property types in your payment authorization.

  2. The requested amount for the payment exceeds the authorized threshold for an order

    This scenario may be caused by unreported price differences pushing the requested amount past the order authorization threshold or products requested by the customer via chat were not properly included in the order by the picker.

    With each declined transaction, the pickers receive push notifications on their apps, advising them about inconsistencies in the order and asking them to make the necessary adjustments before retrying. Once adjusted, the order shouldn't be rejected a second time.

  3. Unacceptable orders

    Orders that receive the error message unacceptable_order may have been canceled by the customer (and due to a momentary connectivity issue, the picker app wasn't synced), they may have already been paid for, or the courier is trying to purchase the products at a different retail partner.

  4. Invalid barcodes

    Most of the time, invalid barcode scenarios are associated with fraud attempts. A screenshot of the barcode screen is taken and the payment is declined; either because that barcode is expired or it has already been applied to another payment.

Handling Communication Failures

Best practice on Rappi Payless implementations mandates having an automatic retry at the POS whenever a communication failure occurs i.e., when the POS doesn't receive a response to its payment authorization request.

Example: The cashier enters a command on the POS to send out a payment authorization request to Rappi. If your requests are configured to timeout after 5 seconds, you may safely have up to 6 automatic authorization attempts within the barcode TTL.

The timeout setting is configurable at the discretion of each retailer, taking into account the latency of their private networks.

Falling Back to Debit Cards

If, even after exhausting retries, the POS is still unable to receive a response for a payment authorization request, the fallback flow MUST kick-in. This is required of ANY Rappi Payless implementation before being released to production.

In this scenario, pickers need to notify Rappi that they need funds released to their debit cards to pay for an order. Since their app is unaware of what's going on between the POS and our backend, the following process validates Rappi can safely release funds to a picker.

1. POS tells the cashier something isn't right
When the POS confirms a communication failure, it will calculate a random pin code and display on its screen with the message "Communication with Rappi is unavailable. The Rappi staff needs this code to pay with debit card". No connectivity is required to calculate the pin code.
2. Picker enters the pin code on the app
From the payment view on the app, our pickers have an option labeled "The Code Doesn't Work". Pressing this button, they're instructed to insert the pin code given by the cashier/POS to have funds released to their debit cards.
3. Rappi checks it's a valid pin code
Since both Rappi and the POS are using a common algorithm to calculate the pin code, this validation can happen even when there's no connectivity between the parts.
4. Picker pays for the purchase with a debit card
The pin code validation triggers the regular flow that releases funds to pickers debit cards
IT SHOULD BE A RARE EXCEPTION

The alternative flow allowing card payments is a must have, but should be a rare exception. Our main KPI in Rappi Payless is having 99.9% of transactions processed by our authorization engine. It means the fallback flow should only kick-in once in every 1000 orders processed.

Calculating the Pin Code

Allowing pickers to switch the payment method to debit card without a proper validation would play against the purpose of implementing Rappi Payless; and prevent both Rappi and its partner retailers from extract the most benefits out of this technology.

Therefore, calculating and validating the pin code when there's no connectivity between client and server requires both parties to use a common algorithm and input variables that are available to both.

The algorithm takes two input variables: the current_date formatted as YYMMDD and the barcode. The pin code is resolved by multiplying the current date by the barcode and extracting the last 6 digits of the result. The pin code is a string and not an integer since its leading digit(s) might be zeros.

/* Javascript example:
* Today is May 15th 2020, and the barcode scanned at the POS was 536710
* The expected pin code would be 405650
*/
const currentDate = 200515
const barcode = 536710
const result = currentDate * barcode // 107618405650
const pinCode = result.slice(-6) // 405650
IN CASE THE PIN CODE FAILS

If the pin code is not validated by the app, the shopper has 5 retries to enter the code correctly. If even the retries fail, the order is cancelled.

Sandbox Setup and Usage

Our sandbox operations were created to accelerate our partners' development and testing, and it's proven to drastically reduce the integration lead time. This is a six-step process. Any store_id can be used on the methods listed in our sandbox documentation.

REMEMBER TO USER THE PROPER URL FOR SANDBOX/PRODUCTION

Please check proper URLs in Servers and Authentication.

Upload Product Catalog

PUT /testing/products

Sandbox This endpoint uploads a catalog of products to your Sandbox environment. It's required to allow the creation of orders for development, testing automation, and quality assurance purposes.
The upload operation performs an upsert, meaning any uploaded products matching an SKU identifier (retail_id) in Rappi's database will be updated with fresh information received in the file, otherwise the new product will be created.

PRODUCT CATALOG IN EXCEL FORMAT

Please use this schema

Request Example

curl --location --request PUT 'https://microservices.dev.rappi.com/api/cpgops-integrations/testing/products' \
--header 'Authorization: {access-token}' \
--header 'Accept: application/json' \
--form 'file=@"/C:/sandbox-product-catalog-upload-c6271795dfac2d3cabb3d7c95ad48cfa.xlsx"'

Request Parameters

ParameterTypeInDescription
body requiredobjectbodyYour sandbox product catalog must be provided in an Excel (.xlsx) file. It must be smaller than 2 MB and adhere to the following schema.
» file requiredstring(binary)body

Responses

StatusDescription
200 OKSuccess–file uploaded
400 Bad RequestThere was a problem with the file you uploaded, e.g., "The max file size is 2MB".
401 UnauthorizedThe request was signed with an invalid or expired authorization token
500 Internal Server ErrorThis is an issue with the server, not the request.

Status Code 200
NameDescription
» message string
The success message will include a file processing id (int) you can use to verify the status of your catalog creation or update, e.g., "The file is being processed. Please check the results using the id 32".

Status Code 400
NameDescription
» code integer
The HTTP error code
enums: 400,
» message string
Free text describing the error
enums:

Verify Product Catalog Upload

GET /testing/products/upload-results/{resultId}

Sandbox This endpoint verifies the status of a product catalog creation or update in your sandbox.

Request Example

curl --location --request GET 'https://microservices.dev.rappi.com/api/cpgops-integrations/testing/products/upload-results/{resultId}' \
--header 'Authorization: {access-token}' \
--header 'Accept: application/json'

Request Parameters

ParameterTypeInDescription
resultId requiredintegerpathThe operation identifier returned from a catalog upload.

Responses

StatusDescription
200 OKSuccess response
400 Bad RequestCatalog processing is still running.
401 UnauthorizedThe request was signed with an invalid or expired authorization token
500 Internal Server ErrorThis is an issue with the server, not the request.

Status Code 400
NameDescription
» code integer
The HTTP error code
enums: 400,
» message string
Free text describing the error
enums:
IMPORTANT!

Subsequent uploads overwrite existing products.

At this point, you're ready to start running test cycles. Each test cycle is made up of multiple orders and four steps. Each order in the test cycle simulates a different scenario. You're not required to consume every single order from each set you generate, but it's important to validate that all scenarios are properly handled.

Generate Dummy Orders

POST /testing/orders

Sandbox This endpoint generates a set of dummy orders with multiple scenarios for your testing and QA routines.

Request Example

curl --location --request POST 'https://microservices.dev.rappi.com/api/cpgops-integrations/testing/orders' \
--header 'Content-Type: application/json' \
--header 'Authorization: {access-token}' \
--data-raw '{
"store_id": {store_id}
}'

Request Parameters

ParameterTypeInDescription
body requiredobjectbody
» store_id requiredintegerbodyUnique store identifier in your systems; usually the store identifier from your ERP.

Responses

StatusDescription
200 OKSuccess–set of dummy orders returned; each scenario in the response body will adhere to the schema{ "order_id": "string", "shopper": "string" }.
400 Bad RequestThe request failed; more details in the response body.
401 UnauthorizedThe request was signed with an invalid or expired authorization token
500 Internal Server ErrorThis is an issue with the server, not the request.

Status Code 200
NameDescription
» valid object

» invalid_state object

» invalid_storekeeper object

» different_retailer object

» invalid_high_price object

» invalid_low_price object


Status Code 400
NameDescription
» code integer
The HTTP error code
enums: 400,
» message string
Free text describing the error
enums:

This operation generates a set of dummy orders for different scenarios:

  
validThe transaction will be authorized if the correct products are scanned.
invalid_stateThe transaction will be declined. The order state in our database isn't in_payment. This simulates when a picker tries to pay for an order
... before the shopping list is resolved (which shouldn't be possible);
... that has already been paid for (at another store or POS);
... that has already been canceled (but syncing with the picker app might have failed)
.
different_retailerThe transaction will be declined. It simulates a picker trying to pay with a barcode generated for a different retailer.
invalid_high_priceThe transaction will be declined. It simulates price differences that were not adjusted by the picker before arriving at the cashier.
invalid_storekeeperThis case is marked for deprecation.
invalid_low_priceThis case is marked for deprecation.

Generate Payment Code

This is the same operation used by our picker apps to render the barcodes on the payment view. The payment code path parameter isn't only part of the payment authorization request, but is also used when you need to retrieve the details of an order.

POST /pos/orders/{order_id}/barcode

Sandbox This endpoint generates a payment code (barcode) associated with an order in our database.

Request Example

curl --location --request GET 'https://microservices.dev.rappi.com/api/cpgops-integrations/pos/orders/{order_id}/barcode' \
--header 'Accept: application/json' \
--header 'Authorization: {access-token}'

Request Parameters

ParameterTypeInDescription
order_id requiredintegerpathUnique identifier of an order created by a customer in the sandbox environment, the id of your dummy orders.

Responses

StatusDescription
200 OKSuccess–barcode object returned.
401 UnauthorizedThe request was signed with an invalid or expired authorization token
404 Not FoundThe resource or record you're requesting doesn't exist.
500 Internal Server ErrorThis is an issue with the server, not the request.

Status Code 200
NameDescription
» order_id string

» barcode string

» status string

» created_at string(date-time)

» sent_at string(date-time)

» expires_at string(date-time)

» show_until string(date-time)

» prefix string

» refreshed boolean

» owner_id integer

» qr object

»» id string

»» order_id string

»» barcode string

»» status string

»» created_at string(date-time)

»» sent_at string(date-time)

»» expires_at string(date-time)

»» show_until string(date-time)

»» prefix string

»» owner_id integer

»» refreshed boolean

Retrieve Order Details

GET /pos/v3/payments/{barcode}/order

This endpoint is invoked when the POS needs to retrieve the order details from Rappi before check-out starts. It SHOULD be used in scenarios where the POS needs the customer's identification or loyalty program number to apply exclusive discounts.

Request Example

curl --location --request GET 'https://microservices.dev.rappi.com/api/cpgops-integrations/pos/v3/payments/{barcode}/order' \
--header 'Accept: application/json' \
--header 'Authorization: {access-token}' \
--header 'x-rappi-channel: B2B'
--header 'x-rappi-country: AR' \

Request Parameters

ParameterTypeInDescription
x-rappi-country requiredstringheaderThe country of operation for a partner retailer. It accepts two-letter country codes in the ISO 3166 alpha-2 format.

Accepts: AR, BR, CL, CO, CR, EC, MX, PA, PE, UY
x-rappi-channel requiredstringheaderThis identifies the type of client interacting with the cashier in store.

Accepts: B2B
barcode requiredstringpathThe payment code scanned from a picker's mobile (or manually keyed-in). This code will always be a random number associated with an order.

Responses

StatusDescription
200 OKSuccess–details of an order associated with the scanned barcode returned.
401 UnauthorizedThe request was signed with an invalid or expired authorization token
404 Not FoundThe resource or record you're requesting doesn't exist.
500 Internal Server ErrorThis is an issue with the server, not the request.

Status Code 200
NameDescription
» orderId string
Unique identifier of an order in Rappi.
» paymentMethod string
Payment method.
» created_at string(date-time)
UTC timestamp when the customer placed the order.
» updated_at string(date-time)
UTC timestamp when the order was updated.
» ready_at string(date-time)
UTC timestamp when the order was ready.
» delivery_schedule string(date-time)
UTC timestamp for the delivery schedule.
» currency string
Currency.
» amount double
Amount.
» store_id string
Store ID.
» customer_preference string
Customer Preference.
» customer object

» delivery_address object

» basket array

Payment Authorization

POST /pos/v3/payments/{barcode}/authorize

This endpoint is invoked when the point of sale (POS) must provide a payment authorization request. The transaction may be declined if Rappi finds inconsistencies against the order details in its database.

Request Example

curl -X POST https://microservices.dev.rappi.com/api/cpgops-integrations/pos/v3/payments/{barcode}/authorize \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}' \
-H 'x-rappi-channel: B2B' \
--data-raw '{
"currency": "BRL",
"amount": 2935,
"basket": [
{
"sku": "string",
"non_refundable": false,
"quantity": 1,
"unit_type": "UN",
"unit_price": 0,
"taxes": [
{
"tax_amount": 0,
"tax_code": "ANY_IVA"
}
]
}
],
"store_id": "12345",
"terminal_id": "26182",
"terminal_transaction_id": "999",
"total_discount": 0,
"total_taxes": 0,
"metadata": {
"cashier_id": "999",
"cashier_name": "Carlos Agudelo"
}
}'

Request Parameters

ParameterTypeInDescription
x-rappi-country requiredstringheaderThe country of operation for a partner retailer. It accepts two-letter country codes in the ISO 3166 alpha-2 format.

Accepts: AR, BR, CL, CO, CR, EC, MX, PA, PE, UY
x-rappi-channel requiredstringheaderThis identifies the type of client interacting with the cashier in store.

Accepts: B2B
barcode requiredstringpathThe code scanned from a picker's mobile (or manually typed-in). This code will always be a random number associated with an order (see fall back option for further information.
body requiredobjectbody
» currencystringbodyCurrency.
» amount requirednumberbodyAmount to be collected by this payment.
» store_idstringbodystore_id.
» terminal_idstringbodyUnique store identifier in your systems; usually the store identifier from your ERP.
» terminal_transaction_idstringbodyUnique transaction identifier in your systems; usually the store identifier from your terminal.
» total_taxesnumberbodyTotal amount of taxes collected on this purchase.
» total_discount requirednumberbodyTotal discount SHOULD be the sum of simple product markdowns, single or multi SKUs bundle discounts, and any discounts associated with the form of payment.
» basket [object]body
» metadata object

Responses

StatusDescription
200 OKSuccess–transaction authorization approved by Rappi.
400 Bad RequestTransaction authorization declined. A dispersion fallback must be performed using the pincode. The POS must review the payless development to adjust the request before accepting more orders.
401 UnauthorizedThe request was signed with an invalid or expired authorization token. A dispersion fallback must be performed using the pincode. The POS must review the payless development to adjust the request before accepting more orders.
403 ForbiddenTransaction authorization forbidden. This error occurs when the barcode and the order to authorize belong to another retailer; it can happen as the result of a badly typed barcode. A dispersion fallback must be performed using the pincode
404 Not FoundInvalid barcode error. This error occurs when the barcode used to authorize the order does not exist or has expired. POS must validate that the payment code is correct. RT must generate a new QR code
406 Not AcceptableTransaction authorization declined. Ask the RT for the Shopper to adjust the order. This error occurs due to the difference between prices and products in the cart.
500 Internal Server ErrorThis is an issue with the server, not the request. A dispersion fallback must be performed using the pincode.

Status Code 200
NameDescription
» message string
The transaction authorization returned on this property is represented by a JWT token.

Status Code 400
NameDescription
» code integer
The HTTP error code
enums: 400,
» message string
Human readable description of the error
enums:

Status Code 403
NameDescription
» code integer
The HTTP error code
enums: 403,
» message string
Human readable description of the error
enums:

Status Code 404
NameDescription
» code integer
The HTTP error code
enums: 404,
» message string
Human readable description of the error
enums:

Status Code 406
NameDescription
» code integer
The HTTP error code
enums: 406,
» message string
Human readable description of the error
enums: Unacceptable order,

Payment Cancellation

POST /pos/v3/payments/{barcode}/cancel

This endpoint is invoked when the point of sale (POS) needs to cancel a payment authorization request.

Cancellation limit

Be aware that it's only possible to cancel orders in the same day they were authorized.

Request Example

curl -X POST https://microservices.dev.rappi.com/api/cpgops-integrations/pos/v3/payments/{barcode}/cancel \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'Authorization: Bearer {access-token}' \
-H 'x-rappi-channel: B2B' \
--data-raw '{
"currency": "BRL",
"amount": 2935,
"basket": [
{
"sku": "string",
"non_refundable": false,
"quantity": 1,
"unit_type": "UN",
"unit_price": 0,
"taxes": [
{
"tax_amount": 0,
"tax_code": "ANY_IVA"
}
]
}
],
"store_id": "12345",
"terminal_id": "26182",
"terminal_transaction_id": "999",
"total_discount": 0,
"total_taxes": 0,
"metadata": {
"cashier_id": "999",
"cashier_name": "Carlos Agudelo"
}
}'

Request Parameters

ParameterTypeInDescription
x-rappi-country requiredstringheaderThe country of operation for a partner retailer. It accepts two-letter country codes in the ISO 3166 alpha-2 format.

Accepts: AR, BR, CL, CO, CR, EC, MX, PA, PE, UY
x-rappi-channel requiredstringheaderThis identifies the type of client interacting with the cashier in store.

Accepts: B2B
barcode requiredstringpathThe code scanned from a picker's mobile (or manually typed-in). This code will always be a random number associated with an order (see fall back option for further information.
body requiredobjectbody
» currencystringbodyCurrency.
» amount requirednumberbodyAmount to be collected by this payment.
» store_idstringbodystore_id.
» terminal_idstringbodyUnique store identifier in your systems; usually the store identifier from your ERP.
» terminal_transaction_idstringbodyUnique transaction identifier in your systems; usually the store identifier from your terminal.
» total_taxesnumberbodyTotal amount of taxes collected on this purchase.
» total_discount requirednumberbodyTotal discount SHOULD be the sum of simple product markdowns, single or multi SKUs bundle discounts, and any discounts associated with the form of payment.
» basket [object]body
» metadata object

Responses

StatusDescription
200 OKSuccess–transaction authorization approved by Rappi.
400 Bad RequestTransaction authorization declined.
401 UnauthorizedThe request was signed with an invalid or expired authorization token.
403 ForbiddenTransaction authorization forbidden.
404 Not FoundInvalid barcode error.
500 Internal Server ErrorThis is an issue with the server, not the request.