pin_pays 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2cbfbd8f959f73fcbf5e6d71bf79699b7a7032e2
4
+ data.tar.gz: 2793f70e8da74a6c84d62025d802c4e0c9cf024c
5
+ SHA512:
6
+ metadata.gz: 93ec2c47b016fab6c1efbf37e15b65faf62cd5f50d85866340b54175755029d4eb28e4cf5f88cbe3f47cde2d7e4c74acbcb8fa517cba2a239992a026d1418607
7
+ data.tar.gz: fd4ea8b8c759818445569ca02cf3fb28654a365941d30e5e6ca0a3a917d7d369e9491183e0c050d6ca9b18ac8879ae9452b2b2cc0c1e000bcaa02cf85512de25
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ *.sublime-project
3
+ *.sublime-workspace
4
+ Gemfile.lock
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ pin_pays
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://www.rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Michael Lilley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,288 @@
1
+ pin_pays
2
+ ========
3
+
4
+ Super simple implementation of the Pin Payments API (pin.com.au) for Ruby and Rails projects.
5
+
6
+ ## Installation
7
+
8
+ PinPays is available as a gem:
9
+
10
+ $ gem install pin_pays
11
+
12
+ ## Setup
13
+
14
+ Configure your secret-key and whether to use the test or live modes:
15
+
16
+ ```ruby
17
+ # config/initializers/pin_pay.rb (for rails)
18
+ PinPays.configure do |config|
19
+ config.key = "your-secret-key" # NB: use the right one for the mode!
20
+ config.mode = :test # or :live
21
+ end
22
+ ```
23
+
24
+ TIP: If you're using Rails 4+, I recommend [using your secrets.yml file for the secret key](https://github.com/mlilley/pin_pays/wiki/Using-Rails-4-secrets.yml-file-for-secret-key).
25
+
26
+ ## Usage
27
+
28
+ PinPays replicates the PinPayments documented API closely. To understand the requirements for method inputs or return values refer to the [PinPayments documentation](https://pin.net.au/docs/api), then refer to [Responses](#Responses) section below to see how PinPays transforms the API json responses before giving them to you.
29
+
30
+ Examples:
31
+
32
+ ```ruby
33
+ # create a customer
34
+ customer = PinPays::Customers:create('moo@cow.com', 'card_token_xyz')
35
+ customer[:token] # => 'cust_token_xyz'
36
+
37
+ # issue a charge
38
+ charge = PinPays::Charges:create({ customer_token: customer[:token], email: 'moo@cow.com', description: 'Pants', amount: 4995, remote_ip: '127.0.0.1'})
39
+ charge[:token] # => 'charge_token_789'
40
+
41
+ # pay a refund
42
+ refund = PinPays::Refunds.create(charge[:token], 2500)
43
+ refund[:token] # => 'refund_token_123'
44
+ ```
45
+
46
+ ### Cards
47
+
48
+ ##### Create a card ([ref](https://pin.net.au/docs/api/cards))
49
+ ```ruby
50
+ PinPays::Cards.create(card)
51
+ ```
52
+ where ```card``` is a hash of the card details
53
+
54
+ ### Customers
55
+
56
+ ##### Create a customer ([ref](https://pin.net.au/docs/api/customers#post-customers))
57
+ ```ruby
58
+ PinPays::Customers.create(email, card)
59
+ ```
60
+ where ```card``` is either a card_token (of a previously created card) or a hash of card details.
61
+
62
+ ##### List all customers ([ref](https://pin.net.au/docs/api/customers#get-customers))
63
+ ```ruby
64
+ PinPays::Customers.list(page=nil)
65
+ ```
66
+ where ```page``` is the results page number to return (optional)
67
+
68
+ ##### Retrieve details of a specific customer ([ref](https://pin.net.au/docs/api/customers#get-customer))
69
+ ```ruby
70
+ PinPays::Customers.show(customer_token)
71
+ ```
72
+
73
+ ##### Update a customer's details ([ref](https://pin.net.au/docs/api/customers#put-customer))
74
+ ```ruby
75
+ PinPays::Customers.update(customer_token, options)
76
+ ```
77
+ where ```options``` is a hash of one or more of: ```email:```, ```card_token:```, or ```card:``` (hash of card details).
78
+
79
+ ##### List all charges for a customer ([ref](https://pin.net.au/docs/api/customers#get-customers-charges))
80
+ ```ruby
81
+ PinPays::Customers.charges(customer_token, page=nil)
82
+ ```
83
+ where ```page``` is the results page number to return (optional)
84
+
85
+ ### Charges
86
+
87
+ ##### Create a charge ([ref](https://pin.net.au/docs/api/charges#post-charges))
88
+ ```ruby
89
+ PinPays::Charges.create(options)
90
+ ```
91
+ where ```options``` contains:
92
+ - (mandatory) ```email:```, ```description:```, ```amount:```, and ```remote_ip:```
93
+ - (mandatory, one of) ```customer_token:```, ```card_token:```, or ```card:``` (hash of card details)
94
+ - (optional) ```currency:```, and ```capture:```
95
+
96
+ ##### Capture a charge ([ref](https://pin.net.au/docs/api/charges#put-charges))
97
+ ```ruby
98
+ PinPays::Charges.capture(charge_token)
99
+ ```
100
+
101
+ ##### List all charges ([ref](https://pin.net.au/docs/api/charges#get-charges))
102
+ ```ruby
103
+ PinPays::Charges.list(page=nil)
104
+ ```
105
+ where ```page``` is the results page number to return (optional)
106
+
107
+ ##### Search for a charge that matches one or more criteria ([ref](https://pin.net.au/docs/api/charges#search-charges))
108
+ ```ruby
109
+ PinPays::Charges.search(criteria)
110
+ ```
111
+ where ```criteria``` is a hash containing one or more criteria to search for.
112
+
113
+ ##### Show details of a specific charge ([ref](https://pin.net.au/docs/api/charges#get-charge))
114
+ ```ruby
115
+ PinPays::Charges.show(charge_token)
116
+ ```
117
+
118
+ ### Refunds
119
+
120
+ ##### Create a refund ([ref](https://pin.net.au/docs/api/refunds#post-refunds))
121
+ ```ruby
122
+ PinPays::Refunds.create(charge_token, amount=nil)
123
+ ```
124
+ where ```amount``` is the amount in cents of the original charge to refund (optional, otherwise the entire amount).
125
+
126
+ ##### List all refunds for a given charge ([ref](https://pin.net.au/docs/api/refunds#get-refunds))
127
+ ```ruby
128
+ PinPays::Refunds.list(charge_token)
129
+ ```
130
+
131
+ ### Responses
132
+
133
+ ##### Success Responses
134
+
135
+ All successful calls return a hash. Fields of the hash are the same as those documented in PinPayments documentation (with some small convenience tweaks):
136
+
137
+ Non-paginated style responses have the parent "response" field omitted. For example, this:
138
+
139
+ ```json
140
+ {
141
+ "response": {
142
+ "token": "rf_ERCQy--Ay6o-NKGiUVcKKA",
143
+ "success": null,
144
+ "amount": 400,
145
+ "currency": "USD",
146
+ "charge": "ch_bZ3RhJnIUZ8HhfvH8CCvfA",
147
+ "created_at": "2012-10-27T13:00:00Z",
148
+ "error_message": null,
149
+ "status_message": "Pending"
150
+ }
151
+ }
152
+ ```
153
+
154
+ becomes this:
155
+
156
+ ```ruby
157
+ {
158
+ token: "rf_ERCQy--Ay6o-NKGiUVcKKA",
159
+ success: null,
160
+ amount: 400,
161
+ currency: "USD",
162
+ charge: "ch_bZ3RhJnIUZ8HhfvH8CCvfA",
163
+ created_at: "2012-10-27T13:00:00Z",
164
+ error_message: null,
165
+ status_message: "Pending"
166
+ }
167
+ ```
168
+
169
+ Note:
170
+ - the parent "response" field is omitted
171
+ - the hash keys are symbols (not strings)
172
+
173
+ Additionally, paginated style responses like this:
174
+
175
+ ```json
176
+ {
177
+ "response": [
178
+ {
179
+ "token": "ch_lfUYEBK14zotCTykezJkfg",
180
+ "success": true,
181
+ "amount": 400,
182
+ "currency": "USD",
183
+ "description": "test charge",
184
+ "email": "roland@pin.net.au",
185
+ "ip_address": "203.192.1.172",
186
+ "created_at": "2012-06-20T03:10:49Z",
187
+ "status_message": "Success!",
188
+ "error_message": null,
189
+ "card": {
190
+ "token": "card_nytGw7koRg23EEp9NTmz9w",
191
+ "display_number": "XXXX-XXXX-XXXX-0000",
192
+ "expiry_month": 6,
193
+ "expiry_year": 2020,
194
+ "name": "Roland Robot",
195
+ "address_line1": "42 Sevenoaks St",
196
+ "address_line2": null,
197
+ "address_city": "Lathlain",
198
+ "address_postcode": "6454",
199
+ "address_state": "WA",
200
+ "address_country": "Australia",
201
+ "scheme": "master"
202
+ },
203
+ "captured": true,
204
+ "authorisation_expired": false,
205
+ "transfer": null,
206
+ "settlement_currency": "AUD"
207
+ }
208
+ ],
209
+ "pagination": {
210
+ "current": 1,
211
+ "per_page": 25,
212
+ "count": 1
213
+ }
214
+ }
215
+ ```
216
+
217
+ Become this:
218
+
219
+ ```ruby
220
+ {
221
+ items: [
222
+ {
223
+ token: "ch_lfUYEBK14zotCTykezJkfg",
224
+ success: true,
225
+ amount: 400,
226
+ currency: "USD",
227
+ description: "test charge",
228
+ email: "roland@pin.net.au",
229
+ ip_address: "203.192.1.172",
230
+ created_at: "2012-06-20T03:10:49Z",
231
+ status_message: "Success!",
232
+ error_message: null,
233
+ card: {
234
+ token: "card_nytGw7koRg23EEp9NTmz9w",
235
+ display_number: "XXXX-XXXX-XXXX-0000",
236
+ expiry_month: 6,
237
+ expiry_year: 2020,
238
+ name: "Roland Robot",
239
+ address_line1: "42 Sevenoaks St",
240
+ address_line2: null,
241
+ address_city: "Lathlain",
242
+ address_postcode: "6454",
243
+ address_state: "WA",
244
+ address_country: "Australia",
245
+ scheme: "master"
246
+ },
247
+ captured: true,
248
+ authorisation_expired: false,
249
+ transfer: null,
250
+ settlement_currency: "AUD"
251
+ }
252
+ ],
253
+ pages: {
254
+ current: 1,
255
+ per_page: 25,
256
+ count: 1
257
+ }
258
+ }
259
+ ```
260
+
261
+ Note that:
262
+ - the "response" field becomes "items"
263
+ - the "pagination" field becomes "pages"
264
+
265
+ The only exception to the above is the ```PinPays::Refunds.list``` call, which returns only an array of hashes.
266
+
267
+
268
+ ##### Error Responses
269
+
270
+ All error responses result in an Exception of the type ```PinPays::ApiError```. The exception contains details about the specific error as properties on it:
271
+
272
+ ```ruby
273
+ ...
274
+ rescue PinPays::ApiError => e
275
+ e.error # -> error code (ie: "insufficient_funds")
276
+ e.error_description # -> error description (ie: "There are not enough funds available to process the requested amount")
277
+ e.charge_token # -> of the failed charge (for charge-related api calls)
278
+ e.messages # -> an array of error messages (useful for displaying to the user on failed form submissions)
279
+ e.raw_response # -> the raw json api response as a ruby hash
280
+ end
281
+ ...
282
+ ```
283
+
284
+ For a list of possible error codes, and example responses see https://pin.net.au/docs/api/test-cards and the remainder of the PinPayments documentation pages.
285
+
286
+ ## License
287
+
288
+ Released under the [MIT license](http://www.opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.test_files = FileList['spec/lib/pin_pays/*_spec.rb']
5
+ t.verbose = true
6
+ end
7
+
8
+ task :default => :test
@@ -0,0 +1,48 @@
1
+ module PinPays
2
+ class Api
3
+ LIVE_URI = 'https://api.pin.net.au/1'
4
+ TEST_URI = 'https://test-api.pin.net.au/1'
5
+
6
+ include HTTParty
7
+
8
+ class << self
9
+
10
+ def setup(config)
11
+ base_uri(config.mode == :live ? LIVE_URI : TEST_URI)
12
+ @@auth = { username: config.key, password: '' }
13
+ end
14
+
15
+ protected
16
+
17
+ def api_get(path, query = nil)
18
+ get(path, query: query, basic_auth: @@auth)
19
+ end
20
+
21
+ def api_post(path, body = nil)
22
+ post(path, body: body, basic_auth: @@auth)
23
+ end
24
+
25
+ def api_put(path, body = nil)
26
+ put(path, body: body, basic_auth: @@auth)
27
+ end
28
+
29
+ def api_response(response)
30
+ raise ApiError.new(response.code, response.parsed_response) unless response.code == 200 || response.code == 201
31
+ symbolize_keys(response.parsed_response['response'])
32
+ end
33
+
34
+ def api_paginated_response(response)
35
+ raise ApiError.new(response.code, response.parsed_response) unless response.code == 200 || response.code == 201
36
+ { items: symbolize_keys(response.parsed_response['response']), pages: symbolize_keys(response.parsed_response['pagination']) }
37
+ end
38
+
39
+ def symbolize_keys(o)
40
+ return o.reduce({}) {|memo, (k, v)| memo.tap { |m| m[k.to_sym] = symbolize_keys(v) }} if o.is_a?(Hash)
41
+ return o.reduce([]) {|memo, v| memo << symbolize_keys(v); memo } if o.is_a?(Array)
42
+ o
43
+ end
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ module PinPays
2
+ class Cards < Api
3
+ PATH = '/cards'
4
+
5
+ class << self
6
+
7
+ # Create a card
8
+ # card - hash of card details
9
+ def create(card)
10
+ api_response(api_post(PATH, card))
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ module PinPays
2
+ class Charges < Api
3
+ PATH = '/charges'
4
+
5
+ class << self
6
+
7
+ # Create a charge for a customer or card
8
+ # Options (hash) containing:
9
+ # (mandatory) email, description, amount, and remote_ip
10
+ # (mandatory, one of) customer_token, card_token, or card (a hash of card details)
11
+ # (optional) currency, capture
12
+ def create(options)
13
+ api_response(api_post(PATH, options))
14
+ end
15
+
16
+ # Captures a previously authorized charge
17
+ def capture(charge_token)
18
+ api_response(api_put("#{PATH}/#{charge_token}/capture"))
19
+ end
20
+
21
+ # Lists all charges
22
+ # Page - the page number to return (optional)
23
+ def list(page = nil)
24
+ options = (page.nil? ? nil : { page: page })
25
+ api_paginated_response(api_get(PATH, options))
26
+ end
27
+
28
+ # Search for charges that match one or more criteria
29
+ # Criteria - a hash of search criteria
30
+ def search(criteria)
31
+ api_paginated_response(api_get("#{PATH}/search", criteria))
32
+ end
33
+
34
+ # Retrieve details of a specific charge
35
+ def show(charge_token)
36
+ api_response(api_get("#{PATH}/#{charge_token}"))
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ module PinPays
2
+ class << self
3
+ attr_accessor :configuration
4
+ end
5
+
6
+ def self.configure
7
+ self.configuration ||= Configuration.new
8
+ yield(configuration)
9
+ Api.setup(configuration)
10
+ end
11
+
12
+ class Configuration
13
+ attr_accessor :key
14
+ attr_accessor :mode
15
+
16
+ def initialize
17
+ @key = nil
18
+ @mode = :test
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module PinPays
2
+ class Customers < Api
3
+ PATH = '/customers'
4
+
5
+ class << self
6
+
7
+ # Creates a customer
8
+ # card - either a card token (string), or a hash of card details
9
+ def create(email, card)
10
+ options = { email: email }
11
+ options = options.merge(card.is_a?(String) ? { card_token: card } : { card: card })
12
+ api_response(api_post(PATH, options))
13
+ end
14
+
15
+ # List all customers
16
+ # Page - the page number to return (optional)
17
+ def list(page = nil)
18
+ options = (page.nil? ? nil : { page: page })
19
+ api_paginated_response(api_get(PATH, options))
20
+ end
21
+
22
+ # Retrieve details of a specific customer
23
+ def show(customer_token)
24
+ api_response(api_get("#{PATH}/#{customer_token}"))
25
+ end
26
+
27
+ # Update a customer
28
+ # Options - a hash specifying one or more of: email, card_token, or card (hash)
29
+ def update(customer_token, options)
30
+ api_response(api_put("#{PATH}/#{customer_token}", options))
31
+ end
32
+
33
+ # List all charges for a customer
34
+ # Page - the page number to return (optional)
35
+ def charges(customer_token, page = nil)
36
+ options = (page.nil? ? nil : { page: page })
37
+ api_paginated_response(api_get("#{PATH}/#{customer_token}/charges", options))
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ module PinPays
2
+ class ApiError < StandardError
3
+
4
+ attr_accessor :status, :error, :error_description, :charge_token, :messages, :raw_response
5
+
6
+ def initialize(status, response)
7
+ @status = status
8
+
9
+ @error = response['error']
10
+ @error_description = response['error_description']
11
+ @charge_token = response['charge_token']
12
+ @messages = response['messages']
13
+
14
+ @raw_response = response
15
+
16
+ #puts "ApiError"
17
+ #puts " #{@status} #{@error} #{@error_description}"
18
+ end
19
+
20
+ def to_s
21
+ "#{@error}"
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module PinPays
2
+ class Refunds < Api
3
+ PATH = '/charges'
4
+
5
+ class << self
6
+
7
+ # Create a refund
8
+ # Amount - amount of charge to refund (optional)
9
+ def create(charge_token, amount = nil)
10
+ options = (amount.nil? ? nil : { amount: amount })
11
+ api_response(api_post("#{PATH}/#{charge_token}/refunds", options))
12
+ end
13
+
14
+ # List all refunds for a charge
15
+ # Note: does not return the usual paginated response. Instead returns only an array.
16
+ def list(charge_token)
17
+ api_response(api_get("#{PATH}/#{charge_token}/refunds"))
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ module PinPays
2
+ VERSION = "0.2.0"
3
+ end
data/lib/pin_pays.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'httparty'
2
+
3
+ require 'pin_pays/version'
4
+ require 'pin_pays/config'
5
+ require 'pin_pays/error'
6
+ require 'pin_pays/api'
7
+ require 'pin_pays/cards'
8
+ require 'pin_pays/customers'
9
+ require 'pin_pays/charges'
10
+ require 'pin_pays/refunds'
11
+
data/pin_pays.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ require File.expand_path('../lib/pin_pays/version.rb', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'pin_pays'
5
+ s.version = PinPays::VERSION
6
+ s.licenses = ['MIT']
7
+ s.summary = "Pin Payments API Implementation for Ruby and Rails"
8
+ s.description = "Provides an implementation of the Pin Payments API (pin.com.au) for Ruby and Rails projects"
9
+ s.authors = ["Michael Lilley"]
10
+ s.email = 'mike@mlilley.com'
11
+ s.homepage = 'https://github.com/mlilley/pin_pays'
12
+
13
+ s.add_dependency "httparty", "~> 0.13"
14
+
15
+ s.add_development_dependency "webmock", '~> 1.17'
16
+ s.add_development_dependency "vcr", '~> 2.9'
17
+ s.add_development_dependency "turn", '~> 0.9'
18
+ s.add_development_dependency "rake", '~> 10.3'
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ end
data/spec/README.txt ADDED
@@ -0,0 +1,12 @@
1
+
2
+ How To Run Tests
3
+ ----------------
4
+
5
+ 1. update spec_helper.rb with your pin payments testing secret key
6
+
7
+ 2. optionally delete spec/fixtures/vcr/*.yml, if you want to run tests against
8
+ the real pin payments api, instead of against previously captured api
9
+ response fixtures.
10
+
11
+ 3. run the tests
12
+ $ rake test
@@ -0,0 +1,53 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://<SECRET_KEY>:@test-api.pin.net.au/1/cards
6
+ body:
7
+ encoding: UTF-8
8
+ string: number=5520000000000000&expiry_month=12&expiry_year=2020&cvc=123&name=Mr%20Test%20Case&address_line1=1%20Long%20St&address_line2=&address_city=Somewhereville&address_postcode=30000&address_state=Somewherestate&address_country=Australia
9
+ headers: {}
10
+ response:
11
+ status:
12
+ code: 201
13
+ message: Created
14
+ headers:
15
+ Cache-Control:
16
+ - max-age=0, private, must-revalidate
17
+ Content-Type:
18
+ - application/json; charset=utf-8
19
+ Date:
20
+ - Sat, 26 Apr 2014 03:53:59 GMT
21
+ Etag:
22
+ - '"591261f1ede0d497395bc465d63e6921"'
23
+ Server:
24
+ - Apache/2.2.20 (Ubuntu)
25
+ Status:
26
+ - '201'
27
+ Strict-Transport-Security:
28
+ - max-age=31536000
29
+ Vary:
30
+ - User-Agent
31
+ X-Powered-By:
32
+ - Phusion Passenger (mod_rails/mod_rack) 3.0.11
33
+ X-Rack-Cache:
34
+ - invalidate, pass
35
+ X-Request-Id:
36
+ - 565552946a61935cf3df15864d12320e
37
+ X-Requested-From:
38
+ - 60.242.140.170
39
+ X-Runtime:
40
+ - '0.059031'
41
+ X-Ua-Compatible:
42
+ - IE=Edge,chrome=1
43
+ Content-Length:
44
+ - '367'
45
+ Connection:
46
+ - keep-alive
47
+ body:
48
+ encoding: UTF-8
49
+ string: '{"response":{"token":"card_j2RKCKH0RE4fhWVa0TXo3A","scheme":"master","display_number":"XXXX-XXXX-XXXX-0000","expiry_month":12,"expiry_year":2020,"name":"Mr
50
+ Test Case","address_line1":"1 Long St","address_line2":"","address_city":"Somewhereville","address_postcode":"30000","address_state":"Somewherestate","address_country":"Australia"},"ip_address":"60.242.140.170"}'
51
+ http_version:
52
+ recorded_at: Sat, 26 Apr 2014 03:53:59 GMT
53
+ recorded_with: VCR 2.9.0