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

Exercise processor failover

Force a processor to report that it didn't process a transaction, and see both of what happens next: a fallback processor approving, and failover running out of processors.

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.

5 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.

Give the merchant somewhere to fail over to

1. Configure a second processor profile and note the primary's id

On your side

The primary profile reports that it did not process, authorization failover selects the fallback profile, and the fallback approves. Requires the merchant to have a second active processor profile. Add a second active loopback processor profile to your sandbox merchant, then copy the id of the profile that authorizes today. That id is what the first run below sends, and it's the only value on this page you supply yourself. A merchant with one profile can still run the second case, and it's refused the same way, but the error code is "ProcessorNotConfigured" because there's no second processor to try.

Reference for this operation

Values this step gives you

  • {{primaryProfileId}} The id of the processor profile that should report it didn't process. Read it off the merchant's processor profiles in the application.

Run both failover outcomes

2. Make the primary profile refuse to process

API call

POST /api/transactions

A control field provokes this run, riding on a transaction custom field. Send "loopback.notProcessedProfileId" or "loopbacknotprocessedprofileid" and the simulator reads them identically. A comma-separated list of processor profile ids. Only the listed profiles report that they did not process, so the primary can refuse while the fallback approves. The amount is the sandbox's guaranteed approval, so whatever comes back is failover's doing and not the amount's. Naming a profile is what separates this from a plain decline. The named profile reports that it didn't process the transaction at all, which is a different thing from refusing it. A decline is an answer and routing accepts it. Failing to process leaves the transaction unanswered and sends routing looking for another processor.

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.notProcessedProfileId", "value": "{{primaryProfileId}}" }
        ],
        "invoiceData": {
          "amounts": { "base": 10.00, "total": 10.00 }
        }
      }'
    .NET
    using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };
    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.notProcessedProfileId", value = "{{primaryProfileId}}" }
        },
        invoiceData = new
        {
            amounts = new { @base = 10.00m, total = 10.00m }
        }
    });
    
    var result = await response.Content.ReadFromJsonAsync<JsonElement>();
    
    if (response.IsSuccessStatusCode)
    {
        // A transaction: approved on whichever profile answered.
        var resultCode = result.GetProperty("responseData").GetProperty("resultCode").GetString();
    }
    else
    {
        // No transaction: no processor took the payment, and it's safe to retry.
        var errorCode = result.GetProperty("error").GetProperty("code").GetString();
    }

    3. Confirm the fallback answered

    On your side

    The sandbox answers with the result code "Ok" and files it under the "Approved" outcome. The message reads "Approved". Nothing on the response says which profile answered, so this is worth confirming in the application rather than in your own code: the transaction's processor profile is the fallback, not the one you named. What your integration sees is an ordinary approval, which is the point. Failover is meant to be invisible to the caller.

    Reference for this operation

    Values this step gives you

      What this step answers with

      Abridged to the properties this step depends on. A real response carries more.

      HTTP 200
      {
        "id": "9f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
        "merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
        "resultCode": "Ok",
        "authorizedAmount": 10.00,
        "responseData": {
          "resultCode": "Ok",
          "resultMessage": "Approved"
        }
      }

      4. Make every profile refuse to process

      API call

      POST /api/transactions

      A control field provokes this run, riding on a transaction custom field. Send "loopback.notProcessed" or "loopbacknotprocessed" and the simulator reads them identically. Set to true and every loopback profile reports that it did not process the transaction, so authorization failover is attempted and then exhausted. The amount is the sandbox's guaranteed approval, so whatever comes back is failover's doing and not the amount's. The same run with nothing left to fail over to. This is the case worth writing code for, and the one almost nobody can reproduce on a live processor.

      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.notProcessed", "value": "true" }
            ],
            "invoiceData": {
              "amounts": { "base": 10.00, "total": 10.00 }
            }
          }'
        .NET
        using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };
        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.notProcessed", value = "true" }
            },
            invoiceData = new
            {
                amounts = new { @base = 10.00m, total = 10.00m }
            }
        });
        
        var result = await response.Content.ReadFromJsonAsync<JsonElement>();
        
        if (response.IsSuccessStatusCode)
        {
            // A transaction: approved on whichever profile answered.
            var resultCode = result.GetProperty("responseData").GetProperty("resultCode").GetString();
        }
        else
        {
            // No transaction: no processor took the payment, and it's safe to retry.
            var errorCode = result.GetProperty("error").GetProperty("code").GetString();
        }

        5. Handle the exhausted case

        On your side

        The API refuses the request with HTTP 409 and the error code "Decline" instead of answering with a transaction. The sandbox files the code under the "Declined" outcome. The message reads "The transaction could not be processed by an available processor." This isn't a refusal by an issuer, even where the code matches one. An issuer's decline answers 200 with the transaction; this answers with an error and no transaction, because no processor ever took the payment. Treating it as a decline tells the payer their card was declined when their card was never asked. It's safe to retry, unlike a decline, so an integration that branches on the status recovers from a processor outage without sending the payer away.

        Reference for this operation

        Values this step gives you

          What this step answers with

          Abridged to the properties this step depends on. A real response carries more.

          HTTP 409
          {
            "error": {
              "code": "Decline",
              "message": "The transaction could not be processed by an available processor."
            }
          }

          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.