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 +7 -0
- data/.gitignore +4 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +288 -0
- data/Rakefile +8 -0
- data/lib/pin_pays/api.rb +48 -0
- data/lib/pin_pays/cards.rb +16 -0
- data/lib/pin_pays/charges.rb +42 -0
- data/lib/pin_pays/config.rb +21 -0
- data/lib/pin_pays/customers.rb +43 -0
- data/lib/pin_pays/error.rb +25 -0
- data/lib/pin_pays/refunds.rb +23 -0
- data/lib/pin_pays/version.rb +3 -0
- data/lib/pin_pays.rb +11 -0
- data/pin_pays.gemspec +21 -0
- data/spec/README.txt +12 -0
- data/spec/fixtures/vcr/cards-create.yml +53 -0
- data/spec/fixtures/vcr/charges-create.yml +52 -0
- data/spec/fixtures/vcr/charges-list.yml +79 -0
- data/spec/fixtures/vcr/charges-show.yml +52 -0
- data/spec/fixtures/vcr/customers-charges.yml +52 -0
- data/spec/fixtures/vcr/customers-create.yml +51 -0
- data/spec/fixtures/vcr/customers-list.yml +60 -0
- data/spec/fixtures/vcr/customers-show.yml +51 -0
- data/spec/fixtures/vcr/customers-update.yml +51 -0
- data/spec/fixtures/vcr/refunds-create.yml +50 -0
- data/spec/fixtures/vcr/refunds-list.yml +50 -0
- data/spec/lib/pin_pays/api_spec.rb +22 -0
- data/spec/lib/pin_pays/cards_spec.rb +40 -0
- data/spec/lib/pin_pays/charges_spec.rb +124 -0
- data/spec/lib/pin_pays/config_spec.rb +30 -0
- data/spec/lib/pin_pays/customers_spec.rb +131 -0
- data/spec/lib/pin_pays/refunds_spec.rb +79 -0
- data/spec/spec_helper.rb +22 -0
- metadata +150 -0
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
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
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
data/lib/pin_pays/api.rb
ADDED
@@ -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,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
|
data/lib/pin_pays.rb
ADDED
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
|