Add Payment Form

There a variety of ways to send payment data to Spreedly. Prior to PCI DSS v3.0 the most common and recommended way was to use a custom payment form on your site that POSTs directly to Spreedly, and which used a transparent redirect to navigate back to your site. Your customers never saw a Spreedly landing page so you retained full control of the UX.

Notice: While this method is still supported, and documented here, with PCI DSS v3.0 (effective January 1, 2015) it no longer has the benefit of minimizing your PCI compliance burden. Instead, use the iFrame payment form to achieve the smallest PCI compliance scope.

Build a payment form

First, build a payment form on your site:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <label for="credit_card_first_name">First name</label>
    <input id="credit_card_first_name" name="credit_card[first_name]" type="text" />

    <label for="credit_card_last_name">Last name</label>
    <input id="credit_card_last_name" name="credit_card[last_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input autocomplete="off" id="credit_card_number" name="credit_card[number]" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input autocomplete="off" id="credit_card_verification_value" name="credit_card[verification_value]" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

The names of the input fields are critical, but other than that you have complete control - you can style, layout, and beautify your payment form however you’d like.

Notice the autocomplete="off" attribute on the card number and verification value fields. This is a way to tell browsers to not store these fields in plaintext somewhere on the computer, which is even more important for customers using public computers.

The utf8 hidden field ensures that older versions of Internet Explorer will correctly encode the strings it sends as UTF-8.

NOTE: you do still need to have an SSL certificate for your payment form page. This gives customers confidence in your payment process, and prevents a malicious middle man from tampering with your payment form before it gets to the customer.

Notice the action attribute - this form POST`s directly to Core. Once Core has recorded the information, it will redirect the browser to the url specified in the redirect_url field, tacking on a token that represents the credit card the customer entered. You should then save that returned token - you’ll use it to actually run the payment. In this case the user would be sent to this url:


http://example.com/transparent_redirect_complete?token=EEifwic1wrGaHaRrdBhfLlYqecZ

Of course you don’t have to add a real credit card in order to test this out: Spreedly has a set of test cards you can use against a Test Gateway, as well as a simple means of triggering specific errors for testing purposes.

Adding email and/or billing address to a payment method

You also have the ability to add additional fields to your payment form to allow you to store things like email, billing address, and/or phone number with the payment method. For example:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <label for="email">Email</label>
    <input id="email" name="email" type="text" />

    <label for="credit_card_first_name">First name</label>
    <input id="credit_card_first_name" name="credit_card[first_name]" type="text" />

    <label for="credit_card_last_name">Last name</label>
    <input id="credit_card_last_name" name="credit_card[last_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input id="credit_card_number" name="credit_card[number]" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input id="credit_card_verification_value" name="credit_card[verification_value]" type="text" />

    <label for="credit_card_company">Company</label>
    <input id="credit_card_company" name="credit_card[company]" type="text" />

    <label for="credit_card_address1">Address</label>
    <input id="credit_card_address1" name="credit_card[address1]" type="text" />

    <label for="credit_card_address2">Address 2</label>
    <input id="credit_card_address2" name="credit_card[address2]" type="text" />

    <label for="credit_card_city">City</label>
    <input id="credit_card_city" name="credit_card[city]" type="text" />

    <label for="credit_card_state">State</label>
    <input id="credit_card_state" name="credit_card[state]" type="text" />

    <label for="credit_card_zip">Zip</label>
    <input id="credit_card_zip" name="credit_card[zip]" type="text" />

    <label for="credit_card_country">Country</label>
    <input id="credit_card_country" name="credit_card[country]" type="text" />

    <label for="credit_card_phone_number">Phone</label>
    <input id="credit_card_phone_number" name="credit_card[phone_number]" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

Which sends the resulting payment method token to the URL at:


http://example.com/transparent_redirect_complete?token=ZUmkkbo5ZO78QIrSDzFtbnCYJL5

Adding shipping address to a payment method

You can add a shipping address to your payment form like so:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <label for="credit_card_">First name</label>
    <input id="credit_card_first_name" name="credit_card[first_name]" type="text" />

    <label for="credit_card_last_name">Last name</label>
    <input id="credit_card_last_name" name="credit_card[last_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input id="credit_card_number" name="credit_card[number]" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input id="credit_card_verification_value" name="credit_card[verification_value]" type="text" />

    <label for="credit_card_shipping_address1">Address</label>
    <input id="credit_card_shipping_address1" name="credit_card[shipping_address1]" type="text" />

    <label for="credit_card_shipping_address2">Address 2</label>
    <input id="credit_card_shipping_address2" name="credit_card[shipping_address2]" type="text" />

    <label for="credit_card_shipping_city">City</label>
    <input id="credit_card_shipping_city" name="credit_card[shipping_city]" type="text" />

    <label for="credit_card_shipping_state">State</label>
    <input id="credit_card_shipping_state" name="credit_card[shipping_state]" type="text" />

    <label for="credit_card_shipping_zip">Zip</label>
    <input id="credit_card_shipping_zip" name="credit_card[shipping_zip]" type="text" />

    <label for="credit_card_shipping_country">Country</label>
    <input id="credit_card_shipping_country" name="credit_card[shipping_country]" type="text" />

    <label for="credit_card_shipping_phone_number">Phone</label>
    <input id="credit_card_shipping_phone_number" name="credit_card[shipping_phone_number]" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

Which sends the resulting payment method token to the URL at:


http://example.com/transparent_redirect_complete?token=DHZ4u64U9ljjYt4Tbs40qlWs8AH

Specifying full name

You may want your payment form to have only one name field rather than a separate field for first_name and last_name. If you’d prefer this option, you can do so with the full_name attribute like so:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <label for="credit_card_full_name">Name</label>
    <input id="credit_card_full_name" name="credit_card[full_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input id="credit_card_number" name="credit_card[number]" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input id="credit_card_verification_value" name="credit_card[verification_value]" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

Which sends the resulting payment method token to the URL at:


http://example.com/transparent_redirect_complete?token=76ueEbjckgNSkkNf30QB1GMULHR

Tracking customers

Since Core uses a transparent redirect to actually receive the data for your customers, you need some way to know what user you’re dealing with when they arrive back at your system. There are two ways to do this: the redirect_url and/or by passing custom data through.

Using the redirect_url

In the transparent redirect form that gets POST'ed to Core, one of the fields is the redirect_url. The site building the form has complete control over what’s put in this field, which means that you can easily drop an identifier for the customer (and/or their order or other identifier) into the url, like so:

http://example.com/store/order/1234/finish

Where “1234” is the order number that needs to be completed. This would be redirected back to you like so:

http://example.com/store/order/1234/finish?token=coretoken

You can thus finish processing the order using the payment method token passed back from Core.

Core is also smart enough to handle query parameters, so this redirect_url:

http://example.com/store/order?id=1234

Will get sent back as:

http://example.com/store/order?id=1234&token=coretoken

Using Passthrough Data

Sometimes you need to pass more information than the redirect_url will comfortably contain, or you need to pass it in a format that’s not easily accommodated by a url. For these cases we’ve included the ability to pass custom data on the Core POST and pull it out using the token that’s passed back to you.

To pass data through, just specify the data fields in your form like so:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <input name="data[how_many]" type="text" value="5" />
    <input name="data[some_other_data]" type="text" value="Here is some additional data" />

    <label for="credit_card_first_name">First name</label>
    <input id="credit_card_first_name" name="credit_card[first_name]" type="text" />

    <label for="credit_card_last_name">Last name</label>
    <input id="credit_card_last_name" name="credit_card[last_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input id="credit_card_number" name="credit_card[number]" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input id="credit_card_verification_value" name="credit_card[verification_value]" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

http://example.com/transparent_redirect_complete?token=DXE98DOtUopZon0AQaZcl3wtwIU

The name attribute for the data fields is important. In the form above, we have ‘data[how_many]’ and 'data[some_other_data]’. When you later inquire about the payment method, you’ll find that the data field holds whatever you passed through at the beginning:


curl https://core.spreedly.com/v1/payment_methods/DXE98DOtUopZon0AQaZcl3wtwIU.xml \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ' \
  -H 'Content-Type: application/xml'

<payment_method>
  <token>UdOMp4OAIbNdcRBjTIVWRYvxhT7</token>
  <created_at type="dateTime">2017-07-27T17:51:40Z</created_at>
  <updated_at type="dateTime">2017-07-27T17:51:40Z</updated_at>
  <email nil="true"/>
  <data>
    <how_many>5</how_many>
    <some_other_data>Here is some additional data</some_other_data>
  </data>
  <storage_state>cached</storage_state>
  <test type="boolean">true</test>
  <last_four_digits>1111</last_four_digits>
  <first_six_digits>411111</first_six_digits>
  <card_type>visa</card_type>
  <first_name>Bob</first_name>
  <last_name>Smith</last_name>
  <month type="integer">2</month>
  <year type="integer">2020</year>
  <address1 nil="true"/>
  <address2 nil="true"/>
  <city nil="true"/>
  <state nil="true"/>
  <zip nil="true"/>
  <country nil="true"/>
  <phone_number nil="true"/>
  <company nil="true"/>
  <full_name>Bob Smith</full_name>
  <eligible_for_card_updater type="boolean">true</eligible_for_card_updater>
  <shipping_address1 nil="true"/>
  <shipping_address2 nil="true"/>
  <shipping_city nil="true"/>
  <shipping_state nil="true"/>
  <shipping_zip nil="true"/>
  <shipping_country nil="true"/>
  <shipping_phone_number nil="true"/>
  <payment_method_type>credit_card</payment_method_type>
  <errors>
  </errors>
  <verification_value>XXX</verification_value>
  <number>XXXX-XXXX-XXXX-1111</number>
  <fingerprint>e3cef43464fc832f6e04f187df25af497994</fingerprint>
</payment_method>


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


#<Spreedly::CreditCard:0x007fe333b35d80
@token="Hl4I0eixA9PdF0XXXfdoKB6rccC",
@created_at="2017-07-27T17:51:41Z",
@updated_at="2017-07-27T17:51:41Z",
@email="",
@storage_state="cached",
@data="<how_many>5</how_many>\n    <some_other_data>Here is some additional data</some_other_data>",
@first_name="Bob",
@last_name="Smith",
@full_name="Bob Smith",
@month="2",
@year="2020",
@number="XXXX-XXXX-XXXX-1111",
@last_four_digits="1111",
@first_six_digits="411111",
@card_type="visa",
@verification_value="XXX",
@address1="",
@address2="",
@city="",
@state="",
@zip="",
@country="",
@phone_number="",
@company="",
@fingerprint="e3cef43464fc832f6e04f187df25af497994",
@eligible_for_card_updater="true",
@errors=[]>

The names you choose for your data fields can be whatever you’d like, as long as they’re using the format of 'data[your_field_name_goes_here]’.

In your data fields, you can use a format like XML or JSON to pass a data structure through if you’d like. Of course, it has to live within the HTML form, so you may need to use something like Base64 to encode it if it ends up being at all complicated. In general though, you probably won’t want to pass through very much data in the transparent redirect since you have your own database to store information and the data field is really intended as a way for you to associate some application specific order information while keeping things simple with one payment form for your customer.

Now comes the important caveat: the data in this field can be tampered with (as can, for that matter, your redirect_url). What that means is that you should never pass any data through that either the customer should not know about, or that the customer should not be able to tamper with. A good example is an item price - if you pass it through the data field on the form, a customer could grab the form, change the price, and thereby give themselves a nice discount.

The easiest way to deal with the tampering issue is to not send through data that is valuable to tamper with. Instead of passing through the total amount, instead pass through the id of the invoice in your system, and use that to look up the invoice and pull the total from there. If you absolutely must passthrough tamper-sensitive data, then you will need to tamper proof it using a standard like HMAC, which is beyond the scope of this documentation.

Validating Payment Methods

If there’s one thing that’s guaranteed, it’s that customers will fat-finger their credit card number or forget to fill out the verification value. So how do you handle validating their input when it’s Core that’s processing the form?

There are two parts to the answer: first of all, how to re-use sensitive data when validation fails. Second, how to validate aspects of the payment information that Core doesn’t require, such as the presence of a billing address.

Re-processing Sensitive Data

If everything goes right, the lifecycle of a purchase with Core looks like:

Form -> POST -> Redirect -> Call Purchase API -> Success

But what if the customer forgets to enter their name and the gateway rejects the transaction? Then you’ll get back an error when making the purchase call that looks like this:


curl https://core.spreedly.com/v1/gateways/LlkjmEk0xNkcWrNixXa1fvNoTP4/purchase.xml \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ' \
  -H 'Content-Type: application/xml' \
  -d '<transaction>
        <payment_method_token>770ZrBI3wS8O9opl9xTJinupaCb</payment_method_token>
        <amount>100</amount>
        <currency_code>USD</currency_code>
    </transaction>'

<transaction>
  <on_test_gateway type="boolean">false</on_test_gateway>
  <created_at type="dateTime">2017-07-27T17:51:57Z</created_at>
  <updated_at type="dateTime">2017-07-27T17:51:57Z</updated_at>
  <succeeded type="boolean">false</succeeded>
  <state>failed</state>
  <token>1gvW5Ufq1uI2uMQOsRJI277ZSSt</token>
  <transaction_type>Purchase</transaction_type>
  <order_id nil="true"/>
  <ip nil="true"/>
  <description nil="true"/>
  <email nil="true"/>
  <merchant_name_descriptor nil="true"/>
  <merchant_location_descriptor nil="true"/>
  <gateway_specific_fields nil="true"/>
  <gateway_specific_response_fields>
  </gateway_specific_response_fields>
  <gateway_transaction_id nil="true"/>
  <gateway_latency_ms nil="true"/>
  <amount type="integer">100</amount>
  <currency_code>USD</currency_code>
  <retain_on_success type="boolean">false</retain_on_success>
  <payment_method_added type="boolean">false</payment_method_added>
  <message key="messages.payment_method_invalid">The payment method is invalid.</message>
  <gateway_token>T11bJAANtTWnxl36GYjKWvbNK0g</gateway_token>
  <shipping_address>
    <name></name>
    <address1 nil="true"/>
    <address2 nil="true"/>
    <city nil="true"/>
    <state nil="true"/>
    <zip nil="true"/>
    <country nil="true"/>
    <phone_number nil="true"/>
  </shipping_address>
  <api_urls>
  </api_urls>
  <payment_method>
    <token>VBFEdnKRA7Ay8uOl7Afql9hr6Z4</token>
    <created_at type="dateTime">2017-07-27T17:51:56Z</created_at>
    <updated_at type="dateTime">2017-07-27T17:51:56Z</updated_at>
    <email nil="true"/>
    <data nil="true"/>
    <storage_state>cached</storage_state>
    <test type="boolean">true</test>
    <last_four_digits>1111</last_four_digits>
    <first_six_digits>411111</first_six_digits>
    <card_type>visa</card_type>
    <first_name></first_name>
    <last_name></last_name>
    <month type="integer">2</month>
    <year type="integer">2020</year>
    <address1 nil="true"/>
    <address2 nil="true"/>
    <city nil="true"/>
    <state nil="true"/>
    <zip nil="true"/>
    <country nil="true"/>
    <phone_number nil="true"/>
    <company nil="true"/>
    <full_name></full_name>
    <eligible_for_card_updater nil="true"/>
    <shipping_address1 nil="true"/>
    <shipping_address2 nil="true"/>
    <shipping_city nil="true"/>
    <shipping_state nil="true"/>
    <shipping_zip nil="true"/>
    <shipping_country nil="true"/>
    <shipping_phone_number nil="true"/>
    <payment_method_type>credit_card</payment_method_type>
    <errors>
      <error attribute="first_name" key="errors.blank">First name can't be blank</error>
      <error attribute="last_name" key="errors.blank">Last name can't be blank</error>
    </errors>
    <verification_value>XXX</verification_value>
    <number>XXXX-XXXX-XXXX-1111</number>
    <fingerprint>e3cef43464fc832f6e04f187df25af497994</fingerprint>
  </payment_method>
</transaction>

What you need to do is redisplay the payment form and let the customer try again, but without requiring them to re-enter their credit card number (which they managed to enter correctly). Notice that in the reply, all the fields are included, but the number is returned as XXXX-XXXX-XXXX-1111. The last four digits of a card are not protected data, so we can return it to you.

You can fill in each of the payment fields with the data passed back in the error reply. If you also pass back the payment_method_token field, filled with the token of the failed payment method, we will replace the sensitive data from the old card with the data from the first submission. This allows you to re-use the data from the incorrect submission without knowing what it is:


<form accept-charset="UTF-8" action="https://core.spreedly.com/v1/payment_methods" method="POST">
  <fieldset>
    <input name="redirect_url" type="hidden" value="http://example.com/transparent_redirect_complete" />
    <input name="environment_key" type="hidden" value="C7cRfNJGODKh4Iu5Ox3PToKjniY" />
    <input name="utf8" type="hidden" value="✓" />

    <input name="payment_method_token" type="hidden" value="770ZrBI3wS8O9opl9xTJinupaCb" />

    <label for="credit_card_first_name">First name</label>
    <input id="credit_card_first_name" name="credit_card[first_name]" type="text" />

    <label for="credit_card_last_name">Last name</label>
    <input id="credit_card_last_name" name="credit_card[last_name]" type="text" />

    <label for="credit_card_number">Card Number</label>
    <input id="credit_card_number" name="credit_card[number]" value="XXXX-XXXX-XXXX-1111" type="text" />

    <label for="credit_card_verification_value">Security Code</label>
    <input id="credit_card_verification_value" name="credit_card[verification_value]" value="XXX" type="text" />

    <label for="credit_card_month">Expires on</label>
    <input id="credit_card_month" name="credit_card[month]" type="text" />
    <input id="credit_card_year" name="credit_card[year]" type="text" />

    <button type='submit'>Submit Payment</button>
  </fieldset>
</form>

If the values for any of the sensitive fields are at all changed from those passed back, the new values will take precendence. This means that if you want the card number for the old card to be used, it must be in the form of “XXXX-XXXX-XXXX-1111”, replacing 1111 with the actual last 4 digits of the old card. Likewise, if you’d like the old verification_value to be used, you must specify XXX for it’s value.

But what if the next attempt fails? Just keep chaining the attempts together and Core will take care of carrying the values forward.

Validating Extra Data

Core only does basic validation on the data passed to it - basically just making sure that it has enough data and in the right format to make a submission to the gateway. That said, there’s a lot of optional data you can pass to Core, and sometimes you want to make sure it’s there or that it’s of a particular format. To do that, you can request the payment method before running a purchase, like so:


curl https://core.spreedly.com/v1/payment_methods/EEifwic1wrGaHaRrdBhfLlYqecZ.xml \
  -u 'C7cRfNJGODKh4Iu5Ox3PToKjniY:4UIuWybmdythfNGPqAqyQnYha6s451ri0fYAo4p3drZUi7q2Jf4b7HKg8etDtoKJ'

<payment_method>
  <token>SGzRGIlNHchPA2Y9LzxVhO8KH1G</token>
  <created_at type="dateTime">2017-07-27T17:52:17Z</created_at>
  <updated_at type="dateTime">2017-07-27T17:52:17Z</updated_at>
  <email nil="true"/>
  <data nil="true"/>
  <storage_state>cached</storage_state>
  <test type="boolean">true</test>
  <last_four_digits>1111</last_four_digits>
  <first_six_digits>411111</first_six_digits>
  <card_type>visa</card_type>
  <first_name>Bob</first_name>
  <last_name>Smith</last_name>
  <month type="integer">2</month>
  <year type="integer">2020</year>
  <address1 nil="true"/>
  <address2 nil="true"/>
  <city nil="true"/>
  <state nil="true"/>
  <zip nil="true"/>
  <country nil="true"/>
  <phone_number nil="true"/>
  <company nil="true"/>
  <full_name>Bob Smith</full_name>
  <eligible_for_card_updater type="boolean">true</eligible_for_card_updater>
  <shipping_address1 nil="true"/>
  <shipping_address2 nil="true"/>
  <shipping_city nil="true"/>
  <shipping_state nil="true"/>
  <shipping_zip nil="true"/>
  <shipping_country nil="true"/>
  <shipping_phone_number nil="true"/>
  <payment_method_type>credit_card</payment_method_type>
  <errors>
  </errors>
  <verification_value>XXX</verification_value>
  <number>XXXX-XXXX-XXXX-1111</number>
  <fingerprint>e3cef43464fc832f6e04f187df25af497994</fingerprint>
</payment_method>


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


#<Spreedly::CreditCard:0x007fe333b3f290
@token="EQPPgxQ5yMwe6FmXToNayZEXCi1",
@created_at="2017-07-27T17:52:18Z",
@updated_at="2017-07-27T17:52:18Z",
@email="",
@storage_state="cached",
@data="",
@first_name="Bob",
@last_name="Smith",
@full_name="Bob Smith",
@month="2",
@year="2020",
@number="XXXX-XXXX-XXXX-1111",
@last_four_digits="1111",
@first_six_digits="411111",
@card_type="visa",
@verification_value="XXX",
@address1="",
@address2="",
@city="",
@state="",
@zip="",
@country="",
@phone_number="",
@company="",
@fingerprint="e3cef43464fc832f6e04f187df25af497994",
@eligible_for_card_updater="true",
@errors=[]>

If you find the information that you want to be required - a billing address is provided, the CVV was filled in, etc. - then you can proceed with running the purchase.

Retain

When a payment method is added to Spreedly it is placed in a “cached” storage state. This state indicates that, unless told otherwise, the card will be purged from Spreedly after a short period of time (several minutes, not hours). If you are performing a one-time transaction where the workflow is 1) collect payment information 2) immediately charge the card then you don’t need to do anything else. However, if you want to charge the card again in the future, or have a longer period of time between collecting the payment information and executing a purchase, then you will need to retain the card.

Store the payment method token

In your app, you should create a server action at the redirect_url location that receives the redirect request from Spreedly and stores the token parameter, tied to the user’s account. Your app is fully responsible for the relationship between your user and its stored payment data in Spreedly (via the token reference).

Other methods

Using a custom form like this is the most common and recommended way to send payment information to Spreedly. However, if this workflow doesn’t work for you you can consider these alternative methods.