pin_pays 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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