Skip to content

Instantly share code, notes, and snippets.

@daniloab
Last active August 26, 2025 14:27
Show Gist options
  • Select an option

  • Save daniloab/b1a2363cde8758ade547500f9aa70c81 to your computer and use it in GitHub Desktop.

Select an option

Save daniloab/b1a2363cde8758ade547500f9aa70c81 to your computer and use it in GitHub Desktop.

Instructions for the Backend Tournament - 2025

Challenge

Your team and/or you need to develop a backend that intermediates payment requests to a payment processing service, called Payment Processor.

image

For each intermediated payment, a financial fee is charged. For example, with a 5% fee for a $100.00 payment request, you would be charged $5.00 and keep $95.00.

However, since real life is tough, this service will face instabilities. Its response times may become very high and it may even become unavailable, responding with HTTP 500 errors. And since we know life is tough, we prepare for these things and create a plan B. The plan B is that there is a second Payment Processor service.

image

Note: The fees on payment processing will not change during the tests, and the default service will always have the lowest fee.

The problem is that this contingency service – called Payment Processor Fallback – charges a higher fee on payments. And it may also face instabilities and downtime! In fact, both services may be unstable and/or unavailable at the same time, because that’s how life goes…

Nothing is so bad that it can’t get worse...

Besides the POST /payments endpoint, it’s also necessary to provide an endpoint that details a summary of processed payments – GET /payments-summary. This endpoint will be used to audit consistency between what your backend processed and what the two Payment Processors processed. Think of it as the Central Bank checking if you’re recording everything properly from time to time.

image

These periodic calls during the Tournament test will compare the responses and, at every inconsistency, a hefty fine will be applied!

Life Really is a Roller Coaster...

To make your life easier and to check the availability of the Payment Processors, each of them provides a health-check endpoint – GET /payments/service-health – that shows if the service is experiencing failures and what the minimum response time is for payment processing. However, this endpoint is limited to one call every five seconds. If this limit is exceeded, an HTTP 429 - Too Many Requests response will be returned. You may use these endpoints to develop the best strategy to pay the lowest fee possible.

Scoring

The scoring criterion for the Backend Tournament will be how much profit your backend managed to achieve at the end of the test. In other words, the more payments you process with the lowest fee, the better. Keep in mind that if inconsistencies are detected by the Central Bank, you will have to pay a 35% fine on your total profit.

There is also a technical scoring criterion. If your backend and the Payment Processors have very fast response times, you can also score points. The metric used for performance will be the p99 (we’ll take the worst 1% response times – the 99th percentile). Starting at a p99 of 10ms or less, you receive a bonus on your total profit of 2% for each 1ms below 11ms.

The formula for the performance bonus percentage is (11 - p99) * 0.02. If the value is negative, the bonus is 0% – there is no penalty for results with p99 greater than 11ms.

Examples:

  • p99 of 10ms = 2% bonus
  • p99 of 9ms = 4% bonus
  • p99 of 5ms = 12% bonus
  • p99 of 1ms = 20% bonus

¹ The percentile will be calculated on top of all HTTP requests made during the test, not just the requests made to your backend.

² All payments will have exactly the same value – no random values will be generated.

Architecture, Restrictions, and Submission

Your backend must follow the following architecture/restrictions.

Web Servers: Have at least two instances of web servers that will respond to POST /payments and GET /payments-summary requests. That means some form of load distribution must occur (usually via a load balancer like nginx, for example).

Containerization: You must provide your backend in docker compose format. All images declared in docker compose (docker-compose.yml) must be publicly available in image registries (https://hub.docker.com/, for example).

You must restrict CPU and Memory usage to 1.5 CPU units and 350MB of memory across all declared services, as you prefer, using the attributes deploy.resources.limits.cpus and deploy.resources.limits.memory as in the example below:

services:
  your-service:
    ...
    deploy:
      resources:
        limits:
          cpus: "0.15"
          memory: "42MB"

Examples of docker-compose.yml here, here, and here .

Port 9999: Your endpoints must be exposed on port 9999 accessible via http://localhost:9999 – example here .

Other restrictions

  • Images must be compatible with linux-amd64.
  • The network mode must be bridgehost mode is not allowed.
  • Privileged mode is not allowed.
  • Use of replicated services is not allowed – this makes resource usage verification harder.

Submission

Important!: The deadline to submit your backend is 2025-08-17 at 23:59:59! Results are expected to be announced on 2025-08-20.

To have your backend officially tested by the Backend Tournament, see the results compared with other submissions, and have your name listed as a participant, you must do the following:

  • Have a public git repository (GitHub, for example) with the source code and all artifacts related to the Tournament submission.
  • Open a PR in this repository adding a directory with your identification in participantes. In this PR you must:
    • Include a README.md explaining the technologies used and a link to the repository with the source code of your submission.
    • Include the docker-compose.yml file at the root of this repository with your dependencies (database scripts, configurations, etc.).
    • Include an info.json file with the following structure to facilitate the collection of used technologies:
{
    "name": "Débora Nis Zanfranceschi",
    "social": ["https://x.com/debora-zan", "https://bsky.app/profile/debora-zan.bsky.social"],
    "source-code-repo": "https://github.com/debora-zan/rinha-de-backend-2025",
    "langs": ["node"],
    "storages": ["postgresql", "redis"],
    "messaging": ["rabbitmq", "nats"],
    "load-balancers": ["nginx"],
    "other-technologies": ["xpto"] // include anything that doesn't fit the other categories
}
  • Example of file structure of a submission PR:
├─ participantes/
|  ├─ debs-node-01/
|  |  ├─ docker-compose.yml
|  |  ├─ info.json
|  |  ├─ nginx.config
|  |  ├─ sql/
|  |  |  ├─ ddl.sql
|  |  |  ├─ dml.sql
|  |  ├─ README.md

Important!

  • Do not include source code in the submission.
  • Do not include logs in the submission.
  • Include ONLY what is necessary for test execution.
  • Pay attention to CPU and memory restrictions.

And How Do I Integrate with the Payment Processors???

The Payment Processors will also run in containers through the docker-compose.yml file (or docker-compose-arm64.yml for hosts that use ARM64 processors like newer MacOS models). The containerization files are in this directory.

It is important to note that your docker-compose.yml must declare the network used by the Payment Processors. This means you first need to bring up the Payment Processors so that their network is created before you bring up your backend. You will also need to include the payment-processor network in the services that will integrate with the Payment Processors, as in this example. Don’t forget to also declare the payment-processor network in your docker-compose.yml, as in this example.

After everything is configured in terms of networks, the two Payment Processors will be available at the following addresses for your services:

Payment Processor Default at http://payment-processor-default:8080 Payment Processor Fallback at http://payment-processor-fallback:8080

Now just reference these two addresses, as in this example.

These two addresses are also accessible via host so you can explore them at the following addresses:

Payment Processor Default at http://localhost:8001 Payment Processor Fallback at http://localhost:8002

Testing Locally

Follow these instructions to test your backend locally.

leonardosegfault kindly wrote a mini setup guide to help with the local configuration to run the tests.

Endpoint Details

Endpoints Your Backend Must Provide

Payments

Main endpoint that receives payment requests to be processed.

POST /payments
{
    "correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
    "amount": 19.90
}

HTTP 2XX
Anything

request correlationId is a required and unique field of type UUID.

amount is a required field of type decimal.

response

Any response in the 2XX range (200, 201, 202, etc.) is valid. The response body will not be validated – it can be anything or even empty.

Payments Summary

This endpoint must return a summary of what has already been processed in terms of payments.

GET /payments-summary?from=2020-07-10T12:34:56.000Z&to=2020-07-10T12:35:56.000Z

HTTP 200 - Ok
{
    "default" : {
        "totalRequests": 43236,
        "totalAmount": 415542345.98
    },
    "fallback" : {
        "totalRequests": 423545,
        "totalAmount": 329347.34
    }
}

request

from is an optional timestamp field in ISO format in UTC (usually 3 hours ahead of Brazil’s time).

to is an optional timestamp field in ISO format in UTC.

response

default.totalRequests is a required integer field.

default.totalAmount is a required decimal field.

fallback.totalRequests is a required integer field.

fallback.totalAmount is a required decimal field.

Important! This endpoint, together with Payments Summary from the Payment Processors, will be called several times during the test for consistency verification. The values must be consistent; otherwise, there will be penalties for inconsistency.

Endpoints the Payment Processors Provide

Your backend must integrate with two Payment Processors. Both services have identical APIs, so the following descriptions apply to both.

Payments

This endpoint receives and processes a payment – it is similar to the Payments endpoint your backend must provide. It’s the main endpoint for integration.

POST /payments
{
    "correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
    "amount": 19.90,
    "requestedAt" : "2025-07-15T12:34:56.000Z"
}

HTTP 200 - Ok
{
    "message": "payment processed successfully"
}

request

correlationId is a required and unique field of type UUID.

amount is a required field of type decimal.

requestedAt is a required timestamp field in ISO format in UTC.

response

message is an always-present field of type string.

Health-Check

This endpoint allows you to check the conditions of the Payments endpoint. This endpoint in both Payment Processors may help you decide the best option for processing a payment.

GET /payments/service-health

HTTP 200 - Ok
{
    "failing": false,
    "minResponseTime": 100
}

request

No request parameters. However, this endpoint enforces a call limit – 1 call every 5 seconds. If exceeded, you will receive an HTTP 429 - Too Many Requests error response.

response

failing is an always-present boolean field indicating whether the Payments endpoint is available. If not, requests to the endpoint will return HTTP5XX errors.

minResponseTime is an always-present integer field indicating the best possible response time for the Payments endpoint. For example, if the returned value is 100, there will be no responses faster than 100ms.

Payment Details

You don’t need to integrate with this endpoint. It’s for troubleshooting, if you want/need it.

GET /payments/{id}

HTTP 200 - Ok
{
    "correlationId": "4a7901b8-7d26-4d9d-aa19-4dc1c7cf60b3",
    "amount": 19.90,
    "requestedAt" : 2025-07-15T12:34:56.000Z
}

request

{id} is a required UUID parameter.

response

correlationId is an always-present UUID field.

amount is an always-present decimal field.

requestedAt is an always-present timestamp field in ISO format in UTC.

Payment Processor Administrative Endpoints

The Payment Processor services include administrative endpoints. These endpoints will be used during the test BY THE TEST SCRIPT and you should not integrate with them in the final version. However, they can be useful to simulate failures, slow response times, verify consistency, etc. All the following endpoints require a token provided in the X-Rinha-Token request header.

Payments Summary

This endpoint is similar to the Payments Summary endpoint you must develop in your backend.

GET /admin/payments-summary?from=2020-07-10T12:34:56.000Z&to=2020-07-10T12:35:56.000Z

HTTP 200 - Ok
{
    "totalRequests": 43236,
    "totalAmount": 415542345.98,
    "totalFee": 415542.98,
    "feePerTransaction": 0.01
}

request

from is an optional timestamp field in ISO format in UTC.

to is an optional timestamp field in ISO format in UTC.

response

totalRequests is an always-present integer field. Shows how many payments were processed in the selected period or all payments if no period is provided.

totalAmount is an always-present decimal field. Shows the sum of all payments processed in the selected period or the sum of all payments if no period is provided.

totalFee is an always-present decimal field. Shows the sum of fees for payments processed in the selected period or all payments if no period is provided.

feePerTransaction is an always-present decimal field. Shows the fee amount per transaction.

Important! This endpoint, together with the Payments Summary your backend must provide, will be called several times during the test for consistency verification. The values must be consistent; otherwise, there will be penalties for inconsistency.

Set Token

This endpoint sets a password for the administrative endpoints. If you change the password in your final submission, the test will be aborted and you will score zero in the Tournament. The initial password is 123 and you can use it for local tests.

PUT /admin/configurations/token
{
    "token" : "any password"
}

HTTP 204 - No Content

request

token is a required string field.

response

N/A

Set Delay

This endpoint configures an intentional delay on the Payments endpoint to simulate a longer response time.

PUT /admin/configurations/delay
{
    "delay" : 235
}

HTTP 204 - No Content

request

delay is a required integer field that defines the milliseconds of response delay in the Payments endpoint.

response

N/A

Set Failure

This endpoint configures an intentional failure on the Payments endpoint to simulate server errors.

PUT /admin/configurations/failure
{
    "failure" : true
}

HTTP 204 - No Content

request

failure is a required boolean field that defines whether the Payments endpoint will return a failure.

response

N/A

Database Purge

This endpoint deletes all payments from the database and is only for development convenience.

POST /admin/purge-payments

HTTP 200 - Ok
{
    "message": "All payments purged."
}

request

N/A

response

message is an always-present string field.

Endpoint Summary

The tables below provide a summary to facilitate an overview of the solution.

Endpoints to be developed

Endpoint Description
POST /payments Intermediates the request for payment processing.
GET /payments-summary Displays details of payment processing requests.

Endpoints available in both Payment Processor services

Endpoint Description
POST /payments Requests payment processing.
GET /payments/service-health Checks the health of the payment endpoint. Limit 1 call every 5 seconds.
GET /payments/{id} Displays details of a payment processing request.
GET /admin/payments-summary Displays details of payment processing requests.
PUT /admin/configurations/token Resets an access token required for all endpoints prefixed with '/admin/'
PUT /admin/configurations/delay Configures the delay on the payments endpoint.
PUT /admin/configurations/failure Configures failure on the payments endpoint.
POST /admin/purge-payments Deletes all payments from the database. For development purposes only.

Other Information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment