View as Markdown

llms.txt

No such blueprint

This instance publishes no blueprint at that address. The catalog lists every one it does publish.

Back to the blueprints

Blueprints Testing scenarios

Simulate processor latency

Make the sandbox processor take its time, then make it answer later than your own client is willing to wait, so your timeout path is something you have run rather than something you have written.

Signed in, you can run this blueprint against your own sandbox merchant one call at a time, with the values from each call threaded into the next. Sign in to run it.

3 steps, 2 API callsPaymentsTransactions

Samples use {{API_KEY}} for your API key and {{BASE_URL}} for this instance's API address. Anything else in double braces is a value an earlier step gave you.

Drive each latency profile

1. Send a sale through a degraded processor

API call

POST /api/transactions

Send "loopback.latencyProfile" or "loopbacklatencyprofile" on a transaction custom field, with the value "slow" on it. A degraded processor. Use this to check your own timeouts and retries. Expect about 500 ms at the median, 2000 ms at the 95th percentile, and 4000 ms at the worst, which is where the sampled delay is clamped. The amount is the sandbox's guaranteed approval, so the only thing this run changes is how long the answer takes. The response still arrives and the sale still approves. What changes is how long your own code was holding the request open, which is the part that breaks first when a processor has a bad afternoon: a connection pool sized for a fast answer runs out, and requests that had nothing wrong with them start failing behind it.

Reference for this operation

Values this step gives you

    cURL
    curl -X POST "{{BASE_URL}}/api/transactions" \
      -H "api-key: {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "transactionType": "Sale",
        "cardData": {
          "cardNumber": "4111111111111111",
          "nameOnCard": "Jane Doe",
          "expirationMonth": 12,
          "expirationYear": 2030,
          "cvv": 123
        },
        "customFields": [
          { "name": "loopback.latencyProfile", "value": "slow" }
        ],
        "invoiceData": {
          "amounts": { "base": 10.00, "total": 10.00 }
        }
      }'
    .NET
    using var http = new HttpClient
    {
        BaseAddress = new Uri("{{BASE_URL}}"),
    
        // Your own budget, not the platform's. Set it to what you ship, then run the
        // step above and watch this throw.
        Timeout = TimeSpan.FromSeconds(5)
    };
    
    http.DefaultRequestHeaders.Add("api-key", "{{API_KEY}}");
    
    var response = await http.PostAsJsonAsync("/api/transactions", new
    {
        transactionType = "Sale",
        cardData = new
        {
            cardNumber = "4111111111111111",
            nameOnCard = "Jane Doe",
            expirationMonth = 12,
            expirationYear = 2030,
            cvv = 123
        },
        customFields = new[]
        {
            new { name = "loopback.latencyProfile", value = "slow" }
        },
        invoiceData = new
        {
            amounts = new { @base = 10.00m, total = 10.00m }
        }
    });
    
    var result = await response.Content.ReadFromJsonAsync<JsonElement>();
    var resultCode = result.GetProperty("responseData").GetProperty("resultCode").GetString();

    2. Send a sale that outlasts a typical client timeout

    API call

    POST /api/transactions

    Send "loopback.latencyProfile" or "loopbacklatencyprofile" on a transaction custom field, with the value "timeout" on it. Long enough to trip most client timeouts. Use this to exercise your timeout path. Expect about 3000 ms at the median, 10000 ms at the 95th percentile, and 15000 ms at the worst, which is where the sampled delay is clamped. The amount is the sandbox's guaranteed approval, so the only thing this run changes is how long the answer takes. Long enough that most HTTP clients give up first. Giving up isn't the same as the payment not happening: the request is still in flight, and it may well approve after your client has stopped listening. This is the outcome you can't tell apart from a failure without asking, and asking is what the safe-retry blueprint below is about.

    Reference for this operation

    Values this step gives you

      cURL
      curl -X POST "{{BASE_URL}}/api/transactions" \
        -H "api-key: {{API_KEY}}" \
        -H "Content-Type: application/json" \
        -d '{
          "transactionType": "Sale",
          "cardData": {
            "cardNumber": "4111111111111111",
            "nameOnCard": "Jane Doe",
            "expirationMonth": 12,
            "expirationYear": 2030,
            "cvv": 123
          },
          "customFields": [
            { "name": "loopback.latencyProfile", "value": "timeout" }
          ],
          "invoiceData": {
            "amounts": { "base": 10.00, "total": 10.00 }
          }
        }'
      .NET
      using var http = new HttpClient
      {
          BaseAddress = new Uri("{{BASE_URL}}"),
      
          // Your own budget, not the platform's. Set it to what you ship, then run the
          // step above and watch this throw.
          Timeout = TimeSpan.FromSeconds(5)
      };
      
      http.DefaultRequestHeaders.Add("api-key", "{{API_KEY}}");
      
      var response = await http.PostAsJsonAsync("/api/transactions", new
      {
          transactionType = "Sale",
          cardData = new
          {
              cardNumber = "4111111111111111",
              nameOnCard = "Jane Doe",
              expirationMonth = 12,
              expirationYear = 2030,
              cvv = 123
          },
          customFields = new[]
          {
              new { name = "loopback.latencyProfile", value = "timeout" }
          },
          invoiceData = new
          {
              amounts = new { @base = 10.00m, total = 10.00m }
          }
      });
      
      var result = await response.Content.ReadFromJsonAsync<JsonElement>();
      var resultCode = result.GetProperty("responseData").GetProperty("resultCode").GetString();

      Decide what your client does about it

      3. Set your own timeout, then decide what happens when it fires

      On your side

      A timeout is a decision about how long you are willing to wait, not a report that nothing happened. Pick one on purpose, log the request you abandoned, and never resend a payment blind: ask what became of it first. Send "loopback.latencyMs" instead of a named profile to hold the answer for an exact number of milliseconds, which is how you pin a run to the boundary your own client sits on. Both fields ride on a transaction custom field. Every profile "loopback.latencyProfile" accepts is listed on the testing page. One constraint applies to the custom-field channel. If the merchant has defined any custom fields at all, every submitted custom-field name has to match one of those definitions, and a name that doesn't match is rejected with a 400. A sandbox merchant with no custom-field definitions accepts any name, which is the usual case. If you get a 400 naming the control field you sent, define a custom field with that name on the merchant.

      Reference for this operation

      Values this step gives you

        Reconnecting to the server

        Could not reconnect

        This session has ended

        Attempt 1

        Your work on this page is still here. Retrying keeps it; reloading starts the page again.

        The server no longer holds this page's state, so it has to be loaded again.