pin_payment 0.0.5 → 0.1.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 +4 -4
- data/.gitignore +3 -0
- data/Gemfile.lock +3 -1
- data/README.md +89 -18
- data/lib/pin_payment/base.rb +86 -5
- data/lib/pin_payment/card.rb +43 -0
- data/lib/pin_payment/charge.rb +37 -16
- data/lib/pin_payment/customer.rb +59 -25
- data/lib/pin_payment/refund.rb +17 -14
- data/lib/pin_payment/version.rb +2 -2
- data/lib/pin_payment.rb +1 -0
- data/pin_payment.gemspec +1 -0
- data/test/fixtures/responses.yml +10 -6
- data/test/test_helper.rb +41 -0
- data/test/test_pin_card.rb +31 -0
- data/test/test_pin_charge.rb +19 -3
- data/test/test_pin_customer.rb +37 -22
- data/test/test_pin_refund.rb +13 -14
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b482e0e3054497d152ef8302a1fa9af339834cf
|
4
|
+
data.tar.gz: 7394ef6e8624ac96d1db2b1e726141c0a918e4df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 719872f68f5a3bdcec2f6be1368bb3a11f71281508b8b05f9a1fcc527b6c520f0a9d434e04a5b3f2bf0110dd43f5735e792c13760e67bb078a4dab931a3a774d
|
7
|
+
data.tar.gz: 66826324dcb94dc9bca096c36037c23d452a64068f6545dc414edbed98d98aa053e25103185fdba09db8187e781c7f449facc90111b65fbded2111fda85d00f1
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pin_payment (0.0
|
4
|
+
pin_payment (0.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
fakeweb (1.3.0)
|
10
10
|
rake (10.0.4)
|
11
|
+
yard (0.8.6.1)
|
11
12
|
|
12
13
|
PLATFORMS
|
13
14
|
ruby
|
@@ -16,3 +17,4 @@ DEPENDENCIES
|
|
16
17
|
fakeweb
|
17
18
|
pin_payment!
|
18
19
|
rake
|
20
|
+
yard
|
data/README.md
CHANGED
@@ -34,7 +34,7 @@ task is up to you), if you are only doing a single transaction, this step is
|
|
34
34
|
not required (but recommended).
|
35
35
|
|
36
36
|
```ruby
|
37
|
-
customer = PinPayment::Customer.create(
|
37
|
+
customer = PinPayment::Customer.create('foo@example.com', params[:card_token])
|
38
38
|
```
|
39
39
|
|
40
40
|
The important information from the returned object is `customer.token`. You
|
@@ -45,12 +45,12 @@ Now you can create charges.
|
|
45
45
|
|
46
46
|
```ruby
|
47
47
|
charge = PinPayment::Charge.create(
|
48
|
-
|
49
|
-
email:
|
50
|
-
amount:
|
51
|
-
currency:
|
52
|
-
description:
|
53
|
-
ip_address:
|
48
|
+
customer: customer, # or you can pass customer.token, either way
|
49
|
+
email: customer.email,
|
50
|
+
amount: 1000,
|
51
|
+
currency: 'USD', # only AUD and USD are supported by pin.net.au
|
52
|
+
description: 'Widgets',
|
53
|
+
ip_address: request.ip
|
54
54
|
)
|
55
55
|
|
56
56
|
if charge.success?
|
@@ -58,17 +58,81 @@ if charge.success?
|
|
58
58
|
end
|
59
59
|
```
|
60
60
|
|
61
|
+
If you don't want to create customer records and just want to create once of
|
62
|
+
charges with the credit card data, this is quite simple also. You can do this
|
63
|
+
in 2 steps if you like, or 1.
|
64
|
+
|
65
|
+
First create the card
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
card = PinPayment::Card.create(
|
69
|
+
number: 5520000000000000,
|
70
|
+
expiry_month: 5,
|
71
|
+
expiry_year: 2014,
|
72
|
+
cvc: 123,
|
73
|
+
name: 'Roland Robot',
|
74
|
+
address_line1: '42 Sevenoaks St',
|
75
|
+
address_city: 'Lathlain',
|
76
|
+
address_postcode: 6454,
|
77
|
+
address_state: 'WA',
|
78
|
+
address_country: 'Australia'
|
79
|
+
)
|
80
|
+
```
|
81
|
+
|
82
|
+
Once you have created the card via the API, you will have a `PinPayment::Card`
|
83
|
+
object, and you can use the charge example code above and replace the customer
|
84
|
+
key in the has with `:card`. The same principle applies here, you can pass
|
85
|
+
`:card` into the charge creation as the card object, or as a simple string being
|
86
|
+
the card token.
|
87
|
+
|
88
|
+
Alternatively, you can skip the card creation step, and pass `:card` to the
|
89
|
+
charge creation as a hash itself, it will create the card for you as part of the
|
90
|
+
charge process. Example
|
91
|
+
|
92
|
+
```ruby
|
93
|
+
charge = PinPayment::Charge.create(
|
94
|
+
email: customer.email,
|
95
|
+
amount: 1000,
|
96
|
+
currency: 'USD', # only AUD and USD are supported by pin.net.au
|
97
|
+
description: 'Widgets',
|
98
|
+
ip_address: request.ip
|
99
|
+
card: {
|
100
|
+
number: 5520000000000000,
|
101
|
+
expiry_month: 5,
|
102
|
+
expiry_year: 2014,
|
103
|
+
cvc: 123,
|
104
|
+
name: 'Roland Robot',
|
105
|
+
address_line1: '42 Sevenoaks St',
|
106
|
+
address_city: 'Lathlain',
|
107
|
+
address_postcode: 6454,
|
108
|
+
address_state: 'WA',
|
109
|
+
address_country: 'Australia'
|
110
|
+
}
|
111
|
+
)
|
112
|
+
```
|
113
|
+
|
114
|
+
You can refund charges as well, either directly on the charge object with
|
115
|
+
`charge.refund!` or you can pass a charge object or token directory into
|
116
|
+
`PinPayment::Refund.create`
|
117
|
+
|
118
|
+
Both the Charge and Customer objects have an `all` method that you can use to
|
119
|
+
iterate over your customers and charges. They simply return an array of their
|
120
|
+
respective objects.
|
121
|
+
|
122
|
+
They also both of course have a `find` method which takes a single argument,
|
123
|
+
being the token. For this reason I highly suggest storing `charge.token` as your
|
124
|
+
payment reference whenever you are creating payments. As well as storing
|
125
|
+
`customer.token` against your customers in your own customer database.
|
126
|
+
|
61
127
|
## TODO
|
62
128
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
doing so, but as above, I've not yet had the need and have always had a
|
71
|
-
`card_token` handy to pass in.
|
129
|
+
* The `all` methods need to deal with pagination.
|
130
|
+
* Implement `PinPayment::Customer.charges`
|
131
|
+
* Implement `PinPayment::Charge.search`
|
132
|
+
* There is more info about a charge in a response than what the API says.
|
133
|
+
E.g. there is info about the fees, refund status, and trasfer status. Need
|
134
|
+
to find out what this data is all about. Since it's undocumented, I am
|
135
|
+
hesitent to implement anything that relies on it as yet.
|
72
136
|
|
73
137
|
## Testing
|
74
138
|
|
@@ -77,8 +141,15 @@ responses that I know the API gives. We're not really here to test the output of
|
|
77
141
|
the API, I think we can safely assume it will always give the same output for
|
78
142
|
the same input, and I don't really want to spam their service every time someone
|
79
143
|
runs the test suite. Nor do I want to hard code my test API keys or expect every
|
80
|
-
developer to create a pin account.
|
81
|
-
|
144
|
+
developer to create a pin account.
|
145
|
+
|
146
|
+
Having said that, you can simply jump into `test/test_helper.rb` and comment out
|
147
|
+
the line that sets up fakeweb, then you can uncomment the lines below that and
|
148
|
+
put your own pin.net.au test API keys into the code and run the tests. Note
|
149
|
+
however that this will create a large amount of customers and charges in your
|
150
|
+
test dashboard on pin.net.au.
|
151
|
+
|
152
|
+
Suggestions on improvement here are welcome though.
|
82
153
|
|
83
154
|
## Contributing
|
84
155
|
|
data/lib/pin_payment/base.rb
CHANGED
@@ -4,21 +4,25 @@ require 'pin_payment/error'
|
|
4
4
|
module PinPayment
|
5
5
|
class Base
|
6
6
|
|
7
|
+
def initialize token, options = {}
|
8
|
+
self.token = token
|
9
|
+
self.class.parse_card_data(options).each{|k,v| send("#{k}=", v) if self.class.attributes.include?(k.to_sym) }
|
10
|
+
end
|
11
|
+
|
7
12
|
protected
|
8
13
|
|
9
|
-
def self.post uri, options
|
14
|
+
def self.post uri, options = {}
|
10
15
|
fetch Net::HTTP::Post, uri, options
|
11
16
|
end
|
12
17
|
|
13
|
-
def self.put uri, options
|
18
|
+
def self.put uri, options = {}
|
14
19
|
fetch Net::HTTP::Put, uri, options
|
15
20
|
end
|
16
21
|
|
17
|
-
def self.get uri, options
|
22
|
+
def self.get uri, options = {}
|
18
23
|
fetch Net::HTTP::Get, uri, options
|
19
24
|
end
|
20
25
|
|
21
|
-
# TODO: Accept card as a hash that would create the card at the same time as the charge
|
22
26
|
def self.fetch klass, uri, options
|
23
27
|
client = Net::HTTP.new(uri.host, uri.port)
|
24
28
|
client.use_ssl = true
|
@@ -36,7 +40,84 @@ module PinPayment
|
|
36
40
|
raise Error::InvalidResponse.new(e.message)
|
37
41
|
end
|
38
42
|
raise(Error.create(response['error'], response['error_description'], response['messages'])) if response['error']
|
39
|
-
response['response']
|
43
|
+
response = response['response']
|
44
|
+
response.is_a?(Hash) ? parse_object_tokens(response) : response.map{|x| parse_object_tokens(x) }
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.parse_card_data hash
|
48
|
+
hash = hash.dup
|
49
|
+
card = hash.delete('card') if hash['card']
|
50
|
+
card = hash.delete(:card) if hash[:card]
|
51
|
+
token = hash.delete('card_token') if hash['card_token']
|
52
|
+
token = hash.delete(:card_token) if hash[:card_token]
|
53
|
+
if card.is_a?(Card) and token and !card.token
|
54
|
+
card.token = token
|
55
|
+
elsif card.is_a?(Hash)
|
56
|
+
card = Card.new(token || card[:token] || card['token'], card)
|
57
|
+
elsif card.is_a?(String)
|
58
|
+
card = Card.new(card)
|
59
|
+
elsif token
|
60
|
+
card = Card.new(token)
|
61
|
+
end
|
62
|
+
hash['card'] = card if card
|
63
|
+
hash
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.parse_customer_data hash
|
67
|
+
hash = hash.dup
|
68
|
+
customer = hash.delete('customer') if hash['customer']
|
69
|
+
customer = hash.delete(:customer) if hash[:customer]
|
70
|
+
token = hash.delete('customer_token') if hash['customer_token']
|
71
|
+
token = hash.delete(:customer_token) if hash[:customer_token]
|
72
|
+
if customer.is_a?(Customer) and token and !customer.token
|
73
|
+
customer.token = token
|
74
|
+
elsif customer.is_a?(String)
|
75
|
+
customer = Customer.new(customer)
|
76
|
+
elsif token
|
77
|
+
customer = Customer.new(token)
|
78
|
+
end
|
79
|
+
hash['customer'] = customer if customer
|
80
|
+
hash
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.parse_charge_data hash
|
84
|
+
hash = hash.dup
|
85
|
+
charge = hash.delete('charge') if hash['charge']
|
86
|
+
charge = hash.delete(:charge) if hash[:charge]
|
87
|
+
token = hash.delete('charge_token') if hash['charge_token']
|
88
|
+
token = hash.delete(:charge_token) if hash[:charge_token]
|
89
|
+
if charge.is_a?(Charge) and token and !charge.token
|
90
|
+
charge.token = token
|
91
|
+
elsif charge.is_a?(String)
|
92
|
+
charge = Charge.new(charge)
|
93
|
+
elsif token
|
94
|
+
charge = Charge.new(token)
|
95
|
+
end
|
96
|
+
hash['charge'] = charge if charge
|
97
|
+
hash
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.parse_object_tokens hash
|
101
|
+
parse_charge_data(parse_customer_data(parse_card_data(hash)))
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.parse_options_for_request attributes, options
|
105
|
+
attributes = attributes.map(&:to_s)
|
106
|
+
options = parse_object_tokens(options.select{|k| attributes.include?(k.to_s) })
|
107
|
+
|
108
|
+
if card = options.delete('card')
|
109
|
+
if card.token
|
110
|
+
options['card_token'] = card.token
|
111
|
+
else
|
112
|
+
# Ruby's Net::HTTP#set_form_data doesn't deal with nested hashes :(
|
113
|
+
card.to_hash.each{|k,v| options["card[#{k}]"] = v }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
options['customer_token'] = options.delete('customer').token if options['customer']
|
118
|
+
options['charge_token'] = options.delete('charge').token if options['charge']
|
119
|
+
|
120
|
+
options
|
40
121
|
end
|
41
122
|
|
42
123
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module PinPayment
|
2
|
+
class Card < Base
|
3
|
+
attr_accessor :token, :display_number, :scheme, :address_line1, :address_line2, :address_city, :address_postcode, :address_state, :address_country
|
4
|
+
protected :token=, :display_number=, :scheme=, :address_line1=, :address_line2=, :address_city=, :address_postcode=, :address_state=, :address_country=
|
5
|
+
|
6
|
+
attr_accessor :number, :expiry_month, :expiry_year, :cvc, :name
|
7
|
+
protected :number, :expiry_month, :expiry_year, :cvc, :name
|
8
|
+
protected :number=, :expiry_month=, :expiry_year=, :cvc=, :name=
|
9
|
+
|
10
|
+
# Use the pin API to create a credit card token, usable for 1 month from creation.
|
11
|
+
#
|
12
|
+
# @param [Hash] card_data
|
13
|
+
# @option card_data [#to_s] :name
|
14
|
+
# @option card_data [#to_s] :number
|
15
|
+
# @option card_data [#to_s] :cvc
|
16
|
+
# @option card_data [#to_s] :address_line1
|
17
|
+
# @option card_data [#to_s] :address_line2 (optional)
|
18
|
+
# @option card_data [#to_s] :address_city
|
19
|
+
# @option card_data [#to_s] :address_postcode
|
20
|
+
# @option card_data [#to_s] :address_country
|
21
|
+
# @option card_data [#to_s] :expiry_month
|
22
|
+
# @option card_data [#to_s] :expiry_year (4 digits required)
|
23
|
+
# @return [PinPayment::Card]
|
24
|
+
def self.create card_data
|
25
|
+
attributes = self.attributes - [:token, :display_number, :scheme] # fix attributes allowed by POST API
|
26
|
+
options = parse_options_for_request(attributes, card_data)
|
27
|
+
response = post(URI.parse(PinPayment.api_url).tap{|uri| uri.path = '/1/cards' }, options)
|
28
|
+
new(response.delete('token'), response)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash]
|
32
|
+
def to_hash
|
33
|
+
{}.tap{|h| self.class.attributes.each{|k| v = send(k) ; h[k] = v if v }}
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def self.attributes
|
39
|
+
[:token, :display_number, :scheme, :address_line1, :address_line2, :address_city, :address_postcode, :address_state, :address_country, :number, :expiry_month, :expiry_year, :cvc, :name]
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/lib/pin_payment/charge.rb
CHANGED
@@ -1,30 +1,51 @@
|
|
1
1
|
module PinPayment
|
2
2
|
class Charge < Base
|
3
|
-
|
3
|
+
attr_accessor :token, :amount, :currency, :description, :email, :ip_address, :created_at, :card, :customer, :success
|
4
|
+
protected :token=, :amount=, :currency=, :description=, :email=, :ip_address=, :created_at=, :card=, :customer=, :success=
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
# Uses the pin API to create a charge.
|
7
|
+
#
|
8
|
+
# @param [Hash] charge_data
|
9
|
+
# @option charge_data [String] :amount *required*
|
10
|
+
# @option charge_data [String] :currency *required* only AUD and USD supported
|
11
|
+
# @option charge_data [String] :email *required*
|
12
|
+
# @option charge_data [String] :description *required*
|
13
|
+
# @option charge_data [String] :ip_address *required*
|
14
|
+
# @option charge_data [String, PinPayment::Customer] :customer can be a customer token or a customer object
|
15
|
+
# @option charge_data [String, PinPayment::Card, Hash] :card can be a card token, hash or a card object
|
16
|
+
# @return [PinPayment::Charge]
|
17
|
+
def self.create charge_data
|
18
|
+
attributes = self.attributes - [:token, :success, :created_at] # fix attributes allowed by POST API
|
19
|
+
options = parse_options_for_request(attributes, charge_data)
|
20
|
+
response = post(URI.parse(PinPayment.api_url).tap{|uri| uri.path = '/1/charges' }, options)
|
21
|
+
new(response.delete('token'), response)
|
16
22
|
end
|
17
23
|
|
24
|
+
# Fetches all of your charges using the pin API.
|
25
|
+
#
|
26
|
+
# @return [Array<PinPayment::Charge>]
|
27
|
+
# TODO: pagination
|
28
|
+
def self.all
|
29
|
+
response = get(URI.parse(PinPayment.api_url).tap{|uri| uri.path = '/1/charges' })
|
30
|
+
response.map{|x| new(x.delete('token'), x) }
|
31
|
+
end
|
32
|
+
|
33
|
+
# Refund a charge via the pin API.
|
34
|
+
#
|
35
|
+
# @return [PinPayment::Refund]
|
18
36
|
def refund!
|
19
|
-
Refund.create
|
37
|
+
Refund.create self
|
20
38
|
end
|
21
39
|
|
40
|
+
# @return [Boolean]
|
22
41
|
def success?
|
23
|
-
|
42
|
+
success == true
|
24
43
|
end
|
25
44
|
|
26
|
-
|
27
|
-
|
45
|
+
protected
|
46
|
+
|
47
|
+
def self.attributes
|
48
|
+
[:token, :amount, :currency, :description, :email, :ip_address, :created_at, :card, :customer, :success]
|
28
49
|
end
|
29
50
|
end
|
30
51
|
end
|
data/lib/pin_payment/customer.rb
CHANGED
@@ -1,32 +1,66 @@
|
|
1
1
|
module PinPayment
|
2
2
|
class Customer < Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
3
|
+
attr_accessor :token, :email, :created_at, :card
|
4
|
+
protected :token=, :email=, :created_at=, :card=
|
5
|
+
|
6
|
+
# Uses the pin API to create a customer.
|
7
|
+
#
|
8
|
+
# @param [String] email the customer's email address
|
9
|
+
# @param [String, PinPayment::Card, Hash] card_or_token the customer's credit card details
|
10
|
+
# @return [PinPayment::Customer]
|
11
|
+
def self.create email, card_or_token = nil
|
12
|
+
attributes = self.attributes - [:token, :created_at]
|
13
|
+
options = parse_options_for_request(attributes, email: email, card: card_or_token)
|
14
|
+
response = post(URI.parse(PinPayment.api_url).tap{|uri| uri.path = '/1/customers' }, options)
|
15
|
+
new(response.delete('token'), response)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Update a customer using the pin API.
|
19
|
+
#
|
20
|
+
# @param [String] token the customer token
|
21
|
+
# @param [String] email the customer's new email address
|
22
|
+
# @param [String, PinPayment::Card, Hash] card_or_token the customer's new credit card details
|
23
|
+
# @return [PinPayment::Customer]
|
24
|
+
def self.update token, email, card_or_token = nil
|
25
|
+
new(token).tap{|c| c.update(email, card_or_token) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# Fetches a customer using the pin API.
|
29
|
+
#
|
30
|
+
# @param [String] token the customer token
|
31
|
+
# @return [PinPayment::Customer]
|
32
|
+
def self.find token
|
33
|
+
response = get(URI.parse(PinPayment.api_url).tap{|uri| uri.path = "/1/customers/#{token}" })
|
34
|
+
new(response.delete('token'), response)
|
16
35
|
end
|
17
36
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
)
|
24
|
-
new.
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
# Fetches all of your customers using the pin API.
|
38
|
+
#
|
39
|
+
# @return [Array<PinPayment::Customer>]
|
40
|
+
# TODO: pagination
|
41
|
+
def self.all
|
42
|
+
response = get(URI.parse(PinPayment.api_url).tap{|uri| uri.path = '/1/customers' })
|
43
|
+
response.map{|x| new(x.delete('token'), x) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Update a customer using the pin API.
|
47
|
+
#
|
48
|
+
# @param [String] email the customer's new email address
|
49
|
+
# @param [String, PinPayment::Card, Hash] card_or_token the customer's new credit card details
|
50
|
+
# @return [PinPayment::Customer]
|
51
|
+
def update email, card_or_token = nil
|
52
|
+
attributes = self.class.attributes - [:token, :created_at]
|
53
|
+
options = self.class.parse_options_for_request(attributes, email: email, card: card_or_token)
|
54
|
+
response = self.class.put(URI.parse(PinPayment.api_url).tap{|uri| uri.path = "/1/customers/#{token}" }, options)
|
55
|
+
self.email = response['email']
|
56
|
+
self.card = response['card']
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
protected
|
61
|
+
|
62
|
+
def self.attributes
|
63
|
+
[:token, :email, :created_at, :card]
|
30
64
|
end
|
31
65
|
|
32
66
|
end
|
data/lib/pin_payment/refund.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
module PinPayment
|
2
2
|
class Refund < Base
|
3
|
-
|
3
|
+
attr_accessor :token, :amount, :currency, :charge, :created_at, :status_message
|
4
|
+
protected :token=, :amount=, :currency=, :charge=, :created_at=, :status_message=, :status_message
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
6
|
+
# Uses the pin API to create a refund.
|
7
|
+
#
|
8
|
+
# @param [String, PinPayment::Charge] charge_or_token the charge (or token of the charge) to refund
|
9
|
+
# @return [PinPayment::Refund]
|
10
|
+
def self.create charge_or_token
|
11
|
+
token = charge_or_token.is_a?(Charge) ? charge_or_token.token : charge_or_token
|
12
|
+
response = post(URI.parse(PinPayment.api_url).tap{|uri| uri.path = "/1/charges/#{token}/refunds" })
|
13
|
+
new(response.delete('token'), response)
|
15
14
|
end
|
16
15
|
|
16
|
+
# @return [Boolean]
|
17
17
|
# TODO: API documentation only shows success as being "null" in the JSON
|
18
18
|
# response, so not sure this is possible. All my refunds on the test site
|
19
19
|
# end up in a "Pending" state so not entirely sure on this one.
|
@@ -21,12 +21,15 @@ module PinPayment
|
|
21
21
|
@success == true
|
22
22
|
end
|
23
23
|
|
24
|
+
# @return [String]
|
24
25
|
def status
|
25
|
-
|
26
|
+
status_message
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
protected
|
30
|
+
|
31
|
+
def self.attributes
|
32
|
+
[:token, :amount, :currency, :charge, :created_at, :status_message]
|
30
33
|
end
|
31
34
|
end
|
32
35
|
end
|
data/lib/pin_payment/version.rb
CHANGED
data/lib/pin_payment.rb
CHANGED
data/pin_payment.gemspec
CHANGED
data/test/fixtures/responses.yml
CHANGED
@@ -3,13 +3,17 @@ charge:
|
|
3
3
|
token_already_used: '{"error":"token_already_used","error_description":"Token already used. Card tokens can only be used once, to create a charge or assign a card to a customer."}'
|
4
4
|
invalid_amount: '{"error":"invalid_resource","error_description":"One or more parameters were missing or invalid.","messages":[{"param":"amount","code":"amount_invalid","message":"Amount must be an integer"},{"param":"amount","code":"amount_invalid","message":"Amount must be more than 1 USD"}]}'
|
5
5
|
success: '{"response":{"token":"ch_BjGW-S6WUisI6mOgpDRimg","success":true,"amount":1000,"currency":"USD","description":"Widgets","email":"foo@example.com","ip_address":"127.0.0.1","created_at":"2013-06-12T23:55:38Z","status_message":"Success!","error_message":null,"card":{"token":"card_qMwnMfpG-olOhfJeyxmrcg","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"},"transfer":[],"amount_refunded":0,"total_fees":null,"merchant_entitlement":null,"refund_pending":false}}'
|
6
|
-
|
6
|
+
create_with_card: '{"response":{"token":"ch_lfUYEBK14zotCTykezJkfg","success":true,"amount":400,"currency":"AUD","description":"test charge","email":"roland@pin.net.au","ip_address":"203.192.1.172","created_at":"2013-06-27T00:39:07Z","status_message":"Success","error_message":null,"card":{"token":"card_jxk5A8fjTtmz6Y81NX9G7w","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"42 Sevenoaks St","address_line2":null,"address_city":"Lathlain","address_postcode":"6454","address_state":"WA","address_country":"Australia"},"transfer":null}}'
|
7
|
+
all: '{"response":[{"token":"ch_lfUYEBK14zotCTykezJkfg","success":true,"amount":400,"currency":"AUD","description":"test charge","email":"roland@pin.net.au","ip_address":"203.192.1.172","created_at":"2013-06-27T00:39:07Z","status_message":"Success","error_message":null,"card":{"token":"card_nytGw7koRg23EEp9NTmz9w","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"42 Sevenoaks St","address_line2":null,"address_city":"Lathlain","address_postcode":"6454","address_state":"WA","address_country":"Australia"},"transfer":null},{"token":"ch_shldvbE5eqBQuyY9Fryhzw","success":true,"amount":400,"currency":"AUD","description":"test charge","email":"roland@pin.net.au","ip_address":"203.192.1.172","created_at":"2013-06-27T00:38:41Z","status_message":"Success","error_message":null,"card":{"token":"card_nytGw7koRg23EEp9NTmz9w","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"42 Sevenoaks St","address_line2":null,"address_city":"Lathlain","address_postcode":"6454","address_state":"WA","address_country":"Australia"},"transfer":null}],"count":2,"pagination":{"current":1,"previous":null,"next":1,"per_page":25,"pages":1,"count":2}}'
|
8
|
+
customer:
|
7
9
|
blank_email: '{"error":"invalid_resource","error_description":"One or more parameters were missing or invalid.","messages":[{"param":"email","code":"email_invalid","message":"Email can''t be blank"}]}'
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
success: '{"response":{"token":"cus__03Cn1lSk3offZ0IGkwpCg","email":"foo@example.com","created_at":"2013-06-12T10:08:30Z","card":{"token":"card_qMwnMfpG-olOhfJeyxmrcg","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"}}}'
|
10
|
+
updated: '{"response":{"token":"cus__03Cn1lSk3offZ0IGkwpCg","email":"changed@example.com","created_at":"2013-06-12T10:08:30Z","card":{"token":"card_qMwnMfpG-olOhfJeyxmrcg","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"}}}'
|
11
|
+
created: '{"response":{"token":"cus__03Cn1lSk3offZ0IGkwpCg","email":"foo@example.com","created_at":"2013-06-12T10:08:30Z","card":{"token":"card_qMwnMfpG-olOhfJeyxmrcg","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"}}}'
|
12
|
+
all: '{"response":[{"token":"cus_OEHdj9KE_OiC9eig0h3IEA","email":"user1@example.com","created_at":"2013-06-25T04:14:35Z","card":{"token":"card_7rviJ2IZM__WC9Dnj0RHJA","display_number":"XXXX-XXXX-XXXX-0000","scheme":"visa","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"}},{"token":"cus_cO2VtF4YzK6i1QBcLsdzOA","email":"user2@example.com","created_at":"2013-06-24T05:53:42Z","card":{"token":"card_bVou2J4hzbDIwe-sQ3ZqdQ","display_number":"XXXX-XXXX-XXXX-0000","scheme":"visa","address_line1":"123 Main St","address_line2":"","address_city":"Melbourne","address_postcode":"3000","address_state":"Victoria","address_country":"Australia"}}],"count":2,"pagination":{"current":1,"previous":null,"next":null,"per_page":25,"pages":1,"count":2}}'
|
13
|
+
create_with_card: '{"response":{"token":"cus_XZg1ULpWaROQCOT5PdwLkQ","email":"roland@pin.net.au","created_at":"2013-06-27T00:24:43Z","card":{"token":"card_nytGw7koRg23EEp9NTmz9w","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"42 Sevenoaks St","address_line2":null,"address_city":"Lathlain","address_postcode":"6454","address_state":"WA","address_country":"Australia"}}}'
|
13
14
|
refund:
|
14
15
|
success: '{"response":{"token":"rf_wMYx5YHKaZAwQgj5rtNuTg","success":null,"amount":1000,"currency":"USD","charge":"ch_BjGW-S6WUisI6mOgpDRimg","created_at":"2013-06-25T03:16:33Z","error_message":null,"status_message":"Pending"}}'
|
15
16
|
duplicate: '{"error":"invalid_resource","error_description":"One or more parameters were missing or invalid.","messages":{"charge":["You have tried to refund more than the original charge"]}}'
|
17
|
+
card:
|
18
|
+
success: '{"response":{"token":"card_nytGw7koRg23EEp9NTmz9w","display_number":"XXXX-XXXX-XXXX-0000","scheme":"master","address_line1":"42 Sevenoaks St","address_line2":null,"address_city":"Lathlain","address_postcode":"6454","address_state":"WA","address_country":"Australia"}}'
|
19
|
+
invalid: '{"error":"invalid_resource","error_description":"One or more parameters were missing or invalid.","messages":[{"param":"number","code":"number_invalid","message":"Number is not a valid credit card number"}]}'
|
data/test/test_helper.rb
CHANGED
@@ -9,3 +9,44 @@ def fixtures
|
|
9
9
|
f['responses'] = YAML.load(File.read File.join(File.dirname(__FILE__), 'fixtures', 'responses.yml'))
|
10
10
|
end
|
11
11
|
end
|
12
|
+
|
13
|
+
def common_setup
|
14
|
+
FakeWeb.allow_net_connect = false
|
15
|
+
|
16
|
+
# If you want to test for realsies, change the above to true, and uncomment
|
17
|
+
# below, after putting in your own test keys
|
18
|
+
|
19
|
+
#PinPayment.secret_key = 'super secret'
|
20
|
+
#PinPayment.public_key = 'not so secret'
|
21
|
+
end
|
22
|
+
|
23
|
+
def card_hash
|
24
|
+
{
|
25
|
+
number: 5520000000000000,
|
26
|
+
expiry_month: 5,
|
27
|
+
expiry_year: 2014,
|
28
|
+
cvc: 123,
|
29
|
+
name: 'Roland Robot',
|
30
|
+
address_line1: '42 Sevenoaks St',
|
31
|
+
address_city: 'Lathlain',
|
32
|
+
address_postcode: 6454,
|
33
|
+
address_state: 'WA',
|
34
|
+
address_country: 'Australia'
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def charge_hash
|
39
|
+
{
|
40
|
+
amount: 400,
|
41
|
+
currency: 'AUD',
|
42
|
+
description: 'test charge',
|
43
|
+
email: 'roland@pin.net.au',
|
44
|
+
ip_address: '203.192.1.172',
|
45
|
+
card: card_hash
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def created_customer
|
50
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/customers', body: fixtures['responses']['customer']['created'])
|
51
|
+
customer = PinPayment::Customer.create('foo@example.com', card_hash)
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestPinCard < MiniTest::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
common_setup
|
6
|
+
end
|
7
|
+
|
8
|
+
def test_successful_card
|
9
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/cards', body: fixtures['responses']['card']['success'])
|
10
|
+
card = PinPayment::Card.create(card_hash)
|
11
|
+
assert_equal 'XXXX-XXXX-XXXX-0000', card.display_number
|
12
|
+
assert_equal 'master', card.scheme
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_invalid_card
|
16
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/cards', body: fixtures['responses']['card']['invalid'])
|
17
|
+
assert_raises PinPayment::Error::InvalidResource do
|
18
|
+
PinPayment::Card.create(card_hash.merge(number: 5520000000000099))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_new_from_hash
|
23
|
+
card = PinPayment::Card.new(nil, card_hash)
|
24
|
+
assert_kind_of PinPayment::Card, card
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_to_hash
|
28
|
+
card = PinPayment::Card.new(nil, card_hash)
|
29
|
+
assert_kind_of Hash, card.to_hash
|
30
|
+
end
|
31
|
+
end
|
data/test/test_pin_charge.rb
CHANGED
@@ -2,19 +2,35 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class TestPinCharge < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
|
5
|
+
common_setup
|
6
6
|
end
|
7
7
|
|
8
8
|
def test_invalid_amount
|
9
9
|
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/charges', body: fixtures['responses']['charge']['invalid_amount'])
|
10
10
|
assert_raises PinPayment::Error::InvalidResource do
|
11
|
-
PinPayment::Charge.create(
|
11
|
+
PinPayment::Charge.create(charge_hash.merge(amount: 10.1))
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
def test_successful_charge
|
16
16
|
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/charges', body: fixtures['responses']['charge']['success'])
|
17
|
-
charge = PinPayment::Charge.create(
|
17
|
+
charge = PinPayment::Charge.create(charge_hash)
|
18
18
|
assert_equal true, charge.success?
|
19
19
|
end
|
20
|
+
|
21
|
+
def test_create_charge_with_card_hash
|
22
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/charges', body: fixtures['responses']['charge']['create_with_card'])
|
23
|
+
charge = PinPayment::Charge.create(charge_hash)
|
24
|
+
assert_equal true, charge.success?
|
25
|
+
assert_kind_of PinPayment::Card, charge.card
|
26
|
+
assert_kind_of String, charge.card.token
|
27
|
+
assert charge.card.token.length > 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_fetch_all_charges
|
31
|
+
FakeWeb.register_uri(:get, 'https://test-api.pin.net.au/1/charges', body: fixtures['responses']['charge']['all'])
|
32
|
+
charges = PinPayment::Charge.all
|
33
|
+
assert_kind_of Array, charges
|
34
|
+
assert_kind_of PinPayment::Charge, charges.first
|
35
|
+
end
|
20
36
|
end
|
data/test/test_pin_customer.rb
CHANGED
@@ -2,39 +2,54 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class TestPinCustomer < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
|
5
|
+
common_setup
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
FakeWeb.register_uri(:
|
8
|
+
def test_create_with_blank_email
|
9
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/customers', body: fixtures['responses']['customer']['blank_email'])
|
10
10
|
assert_raises PinPayment::Error::InvalidResource do
|
11
|
-
PinPayment::Customer.
|
11
|
+
PinPayment::Customer.create(email: nil, card: card_hash)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
PinPayment::Customer.update(email: 'foo@example.com')
|
19
|
-
end
|
15
|
+
def test_create_success
|
16
|
+
customer = created_customer
|
17
|
+
assert_kind_of PinPayment::Customer, customer
|
20
18
|
end
|
21
19
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
20
|
+
def test_direct_update
|
21
|
+
customer = created_customer
|
22
|
+
FakeWeb.register_uri(:put, "https://test-api.pin.net.au/1/customers/#{customer.token}", body: fixtures['responses']['customer']['updated'])
|
23
|
+
customer = PinPayment::Customer.update(customer.token, 'changed@example.com')
|
24
|
+
assert_equal 'changed@example.com', customer.email
|
27
25
|
end
|
28
26
|
|
29
|
-
def
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
def test_object_update
|
28
|
+
customer = created_customer
|
29
|
+
FakeWeb.register_uri(:put, "https://test-api.pin.net.au/1/customers/#{customer.token}", body: fixtures['responses']['customer']['updated'])
|
30
|
+
customer.update('changed@example.com')
|
31
|
+
assert_equal 'changed@example.com', customer.email
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_find_customer
|
35
|
+
customer = created_customer
|
36
|
+
FakeWeb.register_uri(:get, "https://test-api.pin.net.au/1/customers/#{customer.token}", body: fixtures['responses']['customer']['created'])
|
37
|
+
customer = PinPayment::Customer.find(customer.token)
|
38
|
+
assert_kind_of PinPayment::Customer, customer
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_fetch_all_customers
|
42
|
+
FakeWeb.register_uri(:get, 'https://test-api.pin.net.au/1/customers', body: fixtures['responses']['customer']['all'])
|
43
|
+
customers = PinPayment::Customer.all
|
44
|
+
assert_kind_of Array, customers
|
45
|
+
assert_kind_of PinPayment::Customer, customers.first
|
33
46
|
end
|
34
47
|
|
35
|
-
def
|
36
|
-
FakeWeb.register_uri(:
|
37
|
-
customer = PinPayment::Customer.
|
38
|
-
|
48
|
+
def test_create_customer_with_card_hash
|
49
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/customers', body: fixtures['responses']['customer']['create_with_card'])
|
50
|
+
customer = PinPayment::Customer.create('roland@pin.net.au', card_hash)
|
51
|
+
assert_kind_of PinPayment::Card, customer.card
|
52
|
+
assert_kind_of String, customer.card.token
|
53
|
+
assert customer.card.token.length > 0
|
39
54
|
end
|
40
55
|
end
|
data/test/test_pin_refund.rb
CHANGED
@@ -2,30 +2,29 @@ require 'test_helper'
|
|
2
2
|
|
3
3
|
class TestPinRefund < MiniTest::Unit::TestCase
|
4
4
|
def setup
|
5
|
-
|
5
|
+
common_setup
|
6
|
+
FakeWeb.register_uri(:post, 'https://test-api.pin.net.au/1/charges', body: fixtures['responses']['charge']['success'])
|
7
|
+
@charge = PinPayment::Charge.create(charge_hash)
|
6
8
|
end
|
7
9
|
|
8
10
|
def test_duplicate_refund
|
9
|
-
token
|
10
|
-
|
11
|
+
FakeWeb.register_uri(:post, "https://test-api.pin.net.au/1/charges/#{@charge.token}/refunds", body: fixtures['responses']['refund']['success'])
|
12
|
+
PinPayment::Refund.create(@charge)
|
13
|
+
FakeWeb.register_uri(:post, "https://test-api.pin.net.au/1/charges/#{@charge.token}/refunds", body: fixtures['responses']['refund']['duplicate'])
|
11
14
|
assert_raises PinPayment::Error::InvalidResource do
|
12
|
-
PinPayment::Refund.create(
|
15
|
+
PinPayment::Refund.create(@charge)
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
16
|
-
def
|
17
|
-
token
|
18
|
-
|
19
|
-
refund = PinPayment::Refund.create(charge_token: token)
|
19
|
+
def test_direct_refund
|
20
|
+
FakeWeb.register_uri(:post, "https://test-api.pin.net.au/1/charges/#{@charge.token}/refunds", body: fixtures['responses']['refund']['success'])
|
21
|
+
refund = PinPayment::Refund.create(@charge.token)
|
20
22
|
assert_equal 'Pending', refund.status
|
21
23
|
end
|
22
24
|
|
23
|
-
def
|
24
|
-
FakeWeb.register_uri(:post,
|
25
|
-
|
26
|
-
|
27
|
-
FakeWeb.register_uri(:post, "https://test-api.pin.net.au/1/charges/#{charge.token}/refunds", body: fixtures['responses']['refund']['success'])
|
28
|
-
refund = charge.refund!
|
25
|
+
def test_object_refund
|
26
|
+
FakeWeb.register_uri(:post, "https://test-api.pin.net.au/1/charges/#{@charge.token}/refunds", body: fixtures['responses']['refund']['success'])
|
27
|
+
refund = @charge.refund!
|
29
28
|
assert_equal 'Pending', refund.status
|
30
29
|
end
|
31
30
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pin_payment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danial Pearce
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-06-
|
11
|
+
date: 2013-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fakeweb
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: yard
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
description: Pin is the easiest way to accept payments online. See https://pin.net.au/
|
42
56
|
for details.
|
43
57
|
email:
|
@@ -54,6 +68,7 @@ files:
|
|
54
68
|
- Rakefile
|
55
69
|
- lib/pin_payment.rb
|
56
70
|
- lib/pin_payment/base.rb
|
71
|
+
- lib/pin_payment/card.rb
|
57
72
|
- lib/pin_payment/charge.rb
|
58
73
|
- lib/pin_payment/customer.rb
|
59
74
|
- lib/pin_payment/error.rb
|
@@ -62,6 +77,7 @@ files:
|
|
62
77
|
- pin_payment.gemspec
|
63
78
|
- test/fixtures/responses.yml
|
64
79
|
- test/test_helper.rb
|
80
|
+
- test/test_pin_card.rb
|
65
81
|
- test/test_pin_charge.rb
|
66
82
|
- test/test_pin_customer.rb
|
67
83
|
- test/test_pin_refund.rb
|
@@ -93,7 +109,9 @@ summary: Ruby bindings for the Pin API
|
|
93
109
|
test_files:
|
94
110
|
- test/fixtures/responses.yml
|
95
111
|
- test/test_helper.rb
|
112
|
+
- test/test_pin_card.rb
|
96
113
|
- test/test_pin_charge.rb
|
97
114
|
- test/test_pin_customer.rb
|
98
115
|
- test/test_pin_refund.rb
|
99
116
|
- test/test_pin_setup.rb
|
117
|
+
has_rdoc:
|