Troubleshooting Production Issues

Your code is all working and you’re successfully processing payments using the test gateway. You then add a production gateway and try to run a purchase but it doesn’t work. What might the issue be? It turns out this is almost always a credentials issue - a case of not providing the proper authentication parameters to the production gateway. Each gateway requires different credentials and they’re often called different things.

However, beyond speculation, the best way to debug production issues is to look at the communication Spreedly had with the remote party (either a gateway or receiver). Spreedly provides full transparency via the transaction transcript.

Please note that as of December 31, 2022, Spreedly only holds transaction transcripts for 90 days after the initial date of the transaction.

Using the transcript

For every Spreedly transaction that involves a remote API call, the details of that remote call are recorded as the transaction transcript. The human-readable, text-based transcript provides low-level insight into the actual details of the remote call including the request headers, body, response status:


curl https://core.spreedly.com/v1/transactions/KS3oZgWXCfFeirK16anYbijLxR/transcript \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ'


env = Spreedly::Environment.new('C7cRfNJGODKh4Iu5Ox3PToKjniY', '4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ', base_url: 'https://core.spreedly.com')
env.find_transcript("KS3oZgWXCfFeirK16anYbijLxR")

The transcript is scrubbed of sensitive information so you can email it to your support representative at your gateway or receiver.

This gives you an opportunity to simply say that “this is what we sent and how you responded - might you help me understand why you responded this way?” The response you get back can help you to know if there’s something else you need to pass to them or if there’s some requirement setting you might need to adjust in your control panel.

For example, you may want to turn off the CVV requirement or turn off the billing address requirement if your gateway defaults to requiring them. Or, if your gateway requires you to provide a list of valid IP addresses, you may need to add Spreedly’s IP Addresses to your gateway UI.

Timeouts

It’s extremely important to us that the Spreedly API is fast. When you make a call to the API, we want to respond as quickly as possible.

The question is, how long should you wait before making the determination in your code that the Spreedly service isn’t operating as it should? Or, to put it another way, how long should you wait to determine that your application is having difficulty talking to the Spreedly service?

Let’s start by focusing on the API calls which are dependent on an external service like a payment gateway. We’re talking here about calls like purchase and authorize. You want to ensure in cases like this that you don’t set your timeout too low because if you do, that timeout could be reached before your gateway has responded. So from your application’s perspective, the transaction failed because it timed out. The reality though is that the transaction succeeded but took longer than expected to do so.

So how long is a reasonable amount of time to wait? We have seen gateways take up to 60 seconds to respond to requests when under heavy load. Therefore, when Spreedly talks to a gateway, we use an internal timeout of 61 seconds. At that point, we stop waiting for the gateway to respond and we mark the transaction as failed because we timed out talking to the gateway. This means that for your code, you’ll want to use a timeout at least a second or two more than that (perhaps 64 seconds) for the API calls which interact with a gateway. If you set your timeout for less than that, it could lead to cases where your customer has successfully paid you but your application thinks the payment has failed.

For the API calls that don’t interact with a gateway like showing a transaction or retaining a payment method, a much shorter timeout is necessary. We recommend a timeout of 5-10 seconds to determine that there’s an issue talking to the Spreedly service.

Response Codes

You may be interested to know the meaning of the codes available for CVV and/or AVS responses. These can help determine the reason a transaction was declined and inform your next steps.

CVV Response Codes

AttributeDescription
DSuspicious transaction
IFailed data validation check
MMatch
NNo Match
PNot Processed
SShould have been present
UIssuer unable to process request
XCard does not support verification

AVS Response Codes

AttributeDescription
AStreet address matches, but postal code does not match.
BStreet address matches, but postal code not verified.
CStreet address and postal code do not match.
DStreet address and postal code match. Code “M” is equivalent.
EAVS data is invalid or AVS is not allowed for this card type.
FCard member’s name does not match, but billing postal code matches.
GNon-U.S. issuing bank does not support AVS.
HCard member’s name does not match. Street address and postal code match.
IAddress not verified.
JCard member’s name, billing address, and postal code match.
KCard member’s name matches but billing address and billing postal code do not match.
LCard member’s name and billing postal code match, but billing address does not match.
MStreet address and postal code match. Code “D” is equivalent.
NStreet address and postal code do not match. For American Express: Card member’s name, street address and postal code do not match.
OCard member’s name and billing address match, but billing postal code does not match.
PPostal code matches, but street address not verified.
QCard member’s name, billing address, and postal code match.
RSystem unavailable.
SBank does not support AVS.
TCard member’s name does not match, but street address matches.
UAddress information unavailable. Returned if the U.S. bank does not support non-U.S. AVS or if the AVS in a U.S. bank is not functioning properly.
VCard member’s name, billing address, and billing postal code match.
WStreet address does not match, but 9-digit postal code matches.
XStreet address and 9-digit postal code match.
YStreet address and 5-digit postal code match.
ZStreet address does not match, but 5-digit postal code matches.

JSON vs XML Array Structure

There are differences when sending an array of data elements in JSON versus XML. When using the XML version of an endpoint, the request body will have a parent wrapper XML element in plural form, describing what the element contains. Nested child XML elements specify the individual items of the array.

In the example below, you will see the items XML element is the parent, with one or more item XML elements.

<transaction>
  <payment_method_token>56wyNnSmuA6CWYP7w0MiYCVIbW6</payment_method_token>
  <amount>100</amount>
  <currency_code>USD</currency_code>
  <gateway_specific_fields>
    <card_connect>
      <items>
          <item>
            <line_no>1</line_no>
            <material>MATERIAL-1</material>
            <disc_amnt>0</disc_amnt>
            <unit_cost>900</unit_cost>
            <uom>CS</uom>
            <description>DESCRIPTION-1</description>
            <tax_amnt>117</tax_amnt>
            <quantity>1000</quantity>
            <upc>UPC-1</upc>
            <net_amnt>150</net_amnt>
          </item>
          <item>
            <line_no>2</line_no>
            <material>MATERIAL-2</material>
            <disc_amnt>0</disc_amnt>
            <unit_cost>900</unit_cost>
            <uom>CS</uom>
            <description>DESCRIPTION-2</description>
            <tax_amnt>117</tax_amnt>
            <quantity>1000</quantity>
            <upc>UPC-1</upc>
            <net_amnt>150</net_amnt>
          </item>
      </items>
    </card_connect>
  </gateway_specific_fields>
</transaction>

For the JSON version of an endpoint, the request body will have a parent wrapper object in plural form, describing what it contains. A child property in single form holds the array of objects.

In the example below, you will see the items JSON element is an object, with an item property which holds the array of item objects.

{
  "transaction": {
    "payment_method_token": "56wyNnSmuA6CWYP7w0MiYCVIbW6",
    "amount": 1691,
    "currency_code": "USD",
    "gateway_specific_fields": {
      "card_connect": {
        "items": {
          "item": [
            {
              "line_no": "1",
              "material": "MATERIAL-1",
              "disc_amnt": "0",
              "unit_cost": "900",
              "uom": "CS",
              "description": "DESCRIPTION-1",
              "tax_amnt": "117",
              "quantity": "1000",
              "upc": "UPC-1",
              "net_amnt": "150"
            },
            {
              "line_no": "2",
              "material": "MATERIAL-2",
              "disc_amnt": "0",
              "unit_cost": "900",
              "uom": "CS",
              "description": "DESCRIPTION-2",
              "tax_amnt": "117",
              "quantity": "1000",
              "upc": "UPC-1",
              "net_amnt": "150"
            }
          ]
        }
      }
    }
  }
}