tappay_ruby 0.4.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/README.md +267 -0
- data/lib/tappay/card_holder.rb +21 -0
- data/lib/tappay/client.rb +65 -0
- data/lib/tappay/configuration.rb +34 -0
- data/lib/tappay/credit_card/instalment.rb +60 -0
- data/lib/tappay/credit_card/pay.rb +121 -0
- data/lib/tappay/credit_card/refund.rb +31 -0
- data/lib/tappay/endpoints.rb +59 -0
- data/lib/tappay/errors.rb +20 -0
- data/lib/tappay/transaction/query.rb +76 -0
- data/lib/tappay/version.rb +3 -0
- data/lib/tappay.rb +29 -0
- data/lib/tappay_ruby.rb +3 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9f75685c53cf95fc785a6486a6649ede92a9ab175b40c90c89349657e03bba98
|
4
|
+
data.tar.gz: 35462d55532c9ab7da45e9277e53ce91f9788f2e51a1999582b30b246815a2dd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8a1f363e1665d9fd1640d8729a828a6f9978f9ef1e0becfbfb9d9d93ad5f9b94d1a516739a1c2f813d05ca323fa2e6654c8d371868f4bdcc53be10316d67ec76
|
7
|
+
data.tar.gz: 43499dda99a4af42cb4e81216550e41a5929630999455ddcc669a5fd16f2b89b639d1541c521e7228b5dccd79fc7572485d80f8e091e52dd72c44abadf795605
|
data/README.md
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
# TapPay Ruby Gem
|
2
|
+
|
3
|
+
A Ruby library for integrating with TapPay payment services. This gem provides a simple and elegant way to process payments, refunds, and handle instalments through TapPay's payment gateway.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Credit card payments (one-time and tokenized)
|
8
|
+
- Instalment payments
|
9
|
+
- Refund processing
|
10
|
+
- Transaction status queries
|
11
|
+
- Comprehensive error handling
|
12
|
+
- Configurable sandbox/production environments
|
13
|
+
- Environment-based endpoints management
|
14
|
+
- Card holder information management
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
gem 'tappay_ruby'
|
22
|
+
```
|
23
|
+
|
24
|
+
And then execute:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
$ bundle install
|
28
|
+
```
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
$ gem install tappay_ruby
|
34
|
+
```
|
35
|
+
|
36
|
+
## Configuration
|
37
|
+
|
38
|
+
There are several ways to configure the gem:
|
39
|
+
|
40
|
+
### 1. Direct Configuration
|
41
|
+
|
42
|
+
The simplest way to configure the gem:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Tappay.configure do |config|
|
46
|
+
# Environment settings
|
47
|
+
config.mode = Rails.env.production? ? :production : :sandbox
|
48
|
+
|
49
|
+
# Common settings
|
50
|
+
config.partner_key = 'your_partner_key'.freeze
|
51
|
+
config.app_id = 'your_app_id'.freeze
|
52
|
+
config.merchant_id = 'your_merchant_id'.freeze
|
53
|
+
config.instalment_merchant_id = 'your_instalment_merchant_id'.freeze
|
54
|
+
config.currency = 'TWD'.freeze
|
55
|
+
config.vat_number = 'your_vat_number'.freeze
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
### 2. Using Environment Variables
|
60
|
+
|
61
|
+
For better security, you can use environment variables:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
Tappay.configure do |config|
|
65
|
+
config.mode = Rails.env.production? ? :production : :sandbox
|
66
|
+
config.partner_key = ENV['TAPPAY_PARTNER_KEY'].freeze
|
67
|
+
config.app_id = ENV['TAPPAY_APP_ID'].freeze
|
68
|
+
config.merchant_id = ENV['TAPPAY_MERCHANT_ID'].freeze
|
69
|
+
# ... other configurations
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
### 3. Using Rails Credentials
|
74
|
+
|
75
|
+
If you're using Rails, you can use credentials:
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
Tappay.configure do |config|
|
79
|
+
config.mode = Rails.env.production? ? :production : :sandbox
|
80
|
+
config.partner_key = Rails.application.credentials.tappay[:partner_key].freeze
|
81
|
+
config.app_id = Rails.application.credentials.tappay[:app_id].freeze
|
82
|
+
# ... other configurations
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
## Environment-based Endpoints
|
87
|
+
|
88
|
+
The gem automatically handles different endpoints for sandbox and production environments. You don't need to specify the full URLs - just set the mode:
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# For sandbox (default)
|
92
|
+
config.mode = :sandbox # Uses https://sandbox.tappaysdk.com/...
|
93
|
+
|
94
|
+
# For production
|
95
|
+
config.mode = :production # Uses https://prod.tappaysdk.com/...
|
96
|
+
```
|
97
|
+
|
98
|
+
## Card Holder Information
|
99
|
+
|
100
|
+
You can provide cardholder information in two ways:
|
101
|
+
|
102
|
+
### 1. Using CardHolder Object (Recommended)
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# Create a CardHolder object
|
106
|
+
card_holder = Tappay::CardHolder.new(
|
107
|
+
name: 'John Doe',
|
108
|
+
email: 'john@example.com',
|
109
|
+
phone_number: '+886923456789'
|
110
|
+
)
|
111
|
+
|
112
|
+
# Use in payment directly
|
113
|
+
result = Tappay::CreditCard::Pay.by_prime(
|
114
|
+
prime: 'prime_from_tappay_sdk',
|
115
|
+
amount: 100,
|
116
|
+
order_number: 'ORDER-123',
|
117
|
+
card_holder: card_holder # No need to call as_json
|
118
|
+
)
|
119
|
+
```
|
120
|
+
|
121
|
+
### 2. Using Hash Directly
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
result = Tappay::CreditCard::Pay.by_prime(
|
125
|
+
prime: 'prime_from_tappay_sdk',
|
126
|
+
amount: 100,
|
127
|
+
order_number: 'ORDER-123',
|
128
|
+
card_holder: {
|
129
|
+
name: 'John Doe',
|
130
|
+
email: 'john@example.com',
|
131
|
+
phone_number: '+886923456789'
|
132
|
+
}
|
133
|
+
)
|
134
|
+
```
|
135
|
+
|
136
|
+
Both approaches are valid and will work the same way. The CardHolder object provides a more structured way to handle cardholder information and includes validation.
|
137
|
+
|
138
|
+
## Usage
|
139
|
+
|
140
|
+
### Pay by Prime
|
141
|
+
|
142
|
+
Use this method when the customer wants to pay with their credit card without storing the card information. The customer will need to enter their card information for each transaction.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
# Basic payment with prime
|
146
|
+
result = Tappay::CreditCard::Pay.by_prime(
|
147
|
+
prime: 'prime_from_tappay_sdk',
|
148
|
+
amount: 100,
|
149
|
+
order_number: 'ORDER-123',
|
150
|
+
currency: 'TWD',
|
151
|
+
redirect_url: 'https://your-site.com/return',
|
152
|
+
three_domain_secure: true, # Enable 3D secure if needed
|
153
|
+
remember: true, # Set to true if you want to store the card for future payments
|
154
|
+
card_holder: card_holder # Optional cardholder information
|
155
|
+
)
|
156
|
+
|
157
|
+
if result['status'] == 0
|
158
|
+
# Payment successful
|
159
|
+
transaction_id = result['rec_trade_id']
|
160
|
+
if result['card_secret']
|
161
|
+
# If remember is true, you'll get these tokens
|
162
|
+
card_key = result['card_secret']['card_key']
|
163
|
+
card_token = result['card_secret']['card_token']
|
164
|
+
# Store card_key and card_token securely for future payments
|
165
|
+
end
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
### Pay by Token
|
170
|
+
|
171
|
+
Use this method when the customer has opted to save their card information for future purchases. This provides a more convenient checkout experience as customers don't need to re-enter their card information.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# Recurring payment with stored card token
|
175
|
+
result = Tappay::CreditCard::Pay.by_token(
|
176
|
+
card_key: 'stored_card_key',
|
177
|
+
card_token: 'stored_card_token',
|
178
|
+
amount: 100,
|
179
|
+
order_number: 'ORDER-124',
|
180
|
+
currency: 'TWD',
|
181
|
+
redirect_url: 'https://your-site.com/return',
|
182
|
+
three_domain_secure: true, # Enable 3D secure if needed
|
183
|
+
card_holder: card_holder # Optional cardholder information
|
184
|
+
)
|
185
|
+
|
186
|
+
if result['status'] == 0
|
187
|
+
# Payment successful
|
188
|
+
transaction_id = result['rec_trade_id']
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
### Instalment Payment
|
193
|
+
|
194
|
+
```ruby
|
195
|
+
payment = Tappay::CreditCard::Instalment.new(
|
196
|
+
prime: 'prime_from_tappay_sdk',
|
197
|
+
amount: 10000,
|
198
|
+
instalment: 6, # 6 monthly installments
|
199
|
+
details: 'Product description',
|
200
|
+
cardholder: {
|
201
|
+
phone_number: '+886923456789',
|
202
|
+
name: 'John Doe',
|
203
|
+
email: 'john@example.com'
|
204
|
+
}
|
205
|
+
)
|
206
|
+
|
207
|
+
begin
|
208
|
+
result = payment.execute
|
209
|
+
if result['status'] == 0
|
210
|
+
# Payment successful
|
211
|
+
instalment_info = result['instalment_info']
|
212
|
+
number_of_instalments = instalment_info['number_of_instalments']
|
213
|
+
first_payment = instalment_info['first_payment']
|
214
|
+
each_payment = instalment_info['each_payment']
|
215
|
+
end
|
216
|
+
rescue Tappay::PaymentError => e
|
217
|
+
# Handle payment error
|
218
|
+
end
|
219
|
+
```
|
220
|
+
|
221
|
+
### Refund Processing
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
refund = Tappay::CreditCard::Refund.new(
|
225
|
+
transaction_id: 'transaction_id_from_payment',
|
226
|
+
amount: 100
|
227
|
+
)
|
228
|
+
|
229
|
+
begin
|
230
|
+
result = refund.execute
|
231
|
+
if result['status'] == 0
|
232
|
+
# Refund successful
|
233
|
+
end
|
234
|
+
rescue Tappay::RefundError => e
|
235
|
+
# Handle refund error
|
236
|
+
end
|
237
|
+
```
|
238
|
+
|
239
|
+
### Error Handling
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
begin
|
243
|
+
result = Tappay::CreditCard::Pay.by_prime(
|
244
|
+
prime: 'prime_from_tappay_sdk',
|
245
|
+
amount: 100,
|
246
|
+
order_number: 'ORDER-123'
|
247
|
+
)
|
248
|
+
rescue Tappay::PaymentError => e
|
249
|
+
# Handle payment error
|
250
|
+
puts e.message
|
251
|
+
rescue Tappay::ValidationError => e
|
252
|
+
# Handle validation error
|
253
|
+
puts e.message
|
254
|
+
end
|
255
|
+
```
|
256
|
+
|
257
|
+
## Development
|
258
|
+
|
259
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests.
|
260
|
+
|
261
|
+
## Contributing
|
262
|
+
|
263
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/7a6163/tappay.
|
264
|
+
|
265
|
+
## License
|
266
|
+
|
267
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Tappay
|
2
|
+
class CardHolder
|
3
|
+
attr_reader :name, :email, :phone_number
|
4
|
+
|
5
|
+
def initialize(name:, email:, phone_number:)
|
6
|
+
@name = name
|
7
|
+
@email = email
|
8
|
+
@phone_number = phone_number
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_h
|
12
|
+
{
|
13
|
+
name: name,
|
14
|
+
email: email,
|
15
|
+
phone_number: phone_number
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
alias_method :as_json, :to_h
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Tappay
|
5
|
+
class Client
|
6
|
+
include HTTParty
|
7
|
+
|
8
|
+
attr_reader :options
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def post(url, data)
|
15
|
+
@response = self.class.post(
|
16
|
+
url,
|
17
|
+
body: data.to_json,
|
18
|
+
headers: headers,
|
19
|
+
timeout: timeout
|
20
|
+
)
|
21
|
+
|
22
|
+
validate_response
|
23
|
+
@response
|
24
|
+
rescue HTTParty::Error => e
|
25
|
+
raise ConnectionError, "HTTP Request failed: #{e.message}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def headers
|
31
|
+
{
|
32
|
+
'Content-Type' => 'application/json',
|
33
|
+
'x-api-key' => Tappay.configuration.partner_key
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
def timeout
|
38
|
+
25 # seconds
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_response
|
42
|
+
case @response.code
|
43
|
+
when 200
|
44
|
+
data = parse_response
|
45
|
+
unless data['status'].zero?
|
46
|
+
raise APIError.new(data['status'], data['msg'])
|
47
|
+
end
|
48
|
+
when 400
|
49
|
+
raise ValidationError, "Invalid request: #{@response.body}"
|
50
|
+
when 401
|
51
|
+
raise ConfigurationError, "Authentication failed. Check your partner_key."
|
52
|
+
when 404
|
53
|
+
raise ConnectionError, "API endpoint not found: #{@response.request.last_uri}"
|
54
|
+
else
|
55
|
+
raise ConnectionError, "HTTP Request failed with code #{@response.code}: #{@response.body}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_response
|
60
|
+
JSON.parse(@response.body)
|
61
|
+
rescue JSON::ParserError => e
|
62
|
+
raise ConnectionError, "Invalid JSON response: #{e.message}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Tappay
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :partner_key, :merchant_id, :instalment_merchant_id, :app_id, :currency, :vat_number
|
4
|
+
attr_writer :api_version
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@mode = :sandbox
|
8
|
+
@api_version = '2'
|
9
|
+
end
|
10
|
+
|
11
|
+
def api_version
|
12
|
+
@api_version.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
def sandbox?
|
16
|
+
@mode == :sandbox
|
17
|
+
end
|
18
|
+
|
19
|
+
def production?
|
20
|
+
@mode == :production
|
21
|
+
end
|
22
|
+
|
23
|
+
def mode=(value)
|
24
|
+
unless [:sandbox, :production].include?(value.to_sym)
|
25
|
+
raise ArgumentError, "Invalid mode. Must be :sandbox or :production"
|
26
|
+
end
|
27
|
+
@mode = value.to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
def mode
|
31
|
+
@mode ||= :sandbox
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Tappay
|
2
|
+
module CreditCard
|
3
|
+
class Instalment
|
4
|
+
def self.by_prime(options = {})
|
5
|
+
InstalmentByPrime.new(options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.by_token(options = {})
|
9
|
+
InstalmentByToken.new(options)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class InstalmentByPrime < PaymentBase
|
14
|
+
def initialize(options = {})
|
15
|
+
super(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def payment_data
|
19
|
+
super.merge(
|
20
|
+
prime: options[:prime],
|
21
|
+
remember: options[:remember] || false
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def endpoint_url
|
26
|
+
Tappay::Endpoints::CreditCard.payment_by_prime_url
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def additional_required_options
|
32
|
+
[:prime, :cardholder, :instalment]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class InstalmentByToken < PaymentBase
|
37
|
+
def initialize(options = {})
|
38
|
+
super(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def payment_data
|
42
|
+
super.merge(
|
43
|
+
card_key: options[:card_key],
|
44
|
+
card_token: options[:card_token],
|
45
|
+
ccv_prime: options[:ccv_prime]
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def endpoint_url
|
50
|
+
Tappay::Endpoints::CreditCard.payment_by_token_url
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def additional_required_options
|
56
|
+
[:card_key, :card_token, :instalment]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Tappay
|
2
|
+
module CreditCard
|
3
|
+
class PaymentBase < Client
|
4
|
+
def initialize(options = {})
|
5
|
+
super
|
6
|
+
validate_options!
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
post(endpoint_url, payment_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def endpoint_url
|
16
|
+
raise NotImplementedError, "Subclass must implement abstract method 'endpoint_url'"
|
17
|
+
end
|
18
|
+
|
19
|
+
def payment_data
|
20
|
+
{
|
21
|
+
partner_key: Tappay.configuration.partner_key,
|
22
|
+
merchant_id: options[:merchant_id] || Tappay.configuration.merchant_id,
|
23
|
+
amount: options[:amount],
|
24
|
+
details: options[:details],
|
25
|
+
currency: options[:currency] || 'TWD',
|
26
|
+
order_number: options[:order_number],
|
27
|
+
redirect_url: options[:redirect_url],
|
28
|
+
three_domain_secure: options[:three_domain_secure] || false
|
29
|
+
}.tap do |data|
|
30
|
+
data[:cardholder] = card_holder_data if options[:cardholder]
|
31
|
+
data[:instalment] = options[:instalment] if options[:instalment]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def card_holder_data
|
36
|
+
return nil unless options[:cardholder]
|
37
|
+
|
38
|
+
case options[:cardholder]
|
39
|
+
when CardHolder
|
40
|
+
options[:cardholder].to_h
|
41
|
+
when Hash
|
42
|
+
options[:cardholder]
|
43
|
+
else
|
44
|
+
raise ValidationError, "Invalid cardholder format"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_options!
|
49
|
+
required = base_required_options + additional_required_options
|
50
|
+
missing = required.select { |key| options[key].nil? }
|
51
|
+
raise ValidationError, "Missing required options: #{missing.join(', ')}" if missing.any?
|
52
|
+
|
53
|
+
validate_instalment! if options[:instalment]
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def base_required_options
|
59
|
+
[:amount, :details]
|
60
|
+
end
|
61
|
+
|
62
|
+
def additional_required_options
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
|
66
|
+
def validate_instalment!
|
67
|
+
unless options[:instalment].to_i.between?(1, 12)
|
68
|
+
raise ValidationError, "Invalid instalment value. Must be between 1 and 12"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Pay < PaymentBase
|
74
|
+
def self.by_prime(options = {})
|
75
|
+
PayByPrime.new(options)
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.by_token(options = {})
|
79
|
+
PayByToken.new(options)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class PayByPrime < PaymentBase
|
84
|
+
def payment_data
|
85
|
+
super.merge(
|
86
|
+
prime: options[:prime],
|
87
|
+
remember: options[:remember] || false
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
def endpoint_url
|
92
|
+
Tappay::Endpoints::CreditCard.payment_by_prime_url
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def additional_required_options
|
98
|
+
[:prime]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class PayByToken < PaymentBase
|
103
|
+
def payment_data
|
104
|
+
super.merge(
|
105
|
+
card_key: options[:card_key],
|
106
|
+
card_token: options[:card_token]
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
def endpoint_url
|
111
|
+
Tappay::Endpoints::CreditCard.payment_by_token_url
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def additional_required_options
|
117
|
+
[:card_key, :card_token, :currency]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Tappay
|
2
|
+
module CreditCard
|
3
|
+
class Refund < Client
|
4
|
+
def initialize(options = {})
|
5
|
+
super
|
6
|
+
validate_options!
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
post(Endpoints::CreditCard.refund_url, refund_data)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def refund_data
|
16
|
+
{
|
17
|
+
partner_key: Tappay.configuration.partner_key,
|
18
|
+
rec_trade_id: options[:transaction_id],
|
19
|
+
amount: options[:amount]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def validate_options!
|
24
|
+
required = [:transaction_id, :amount]
|
25
|
+
missing = required.select { |key| options[key].nil? }
|
26
|
+
|
27
|
+
raise ValidationError, "Missing required options: #{missing.join(', ')}" if missing.any?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tappay
|
4
|
+
module Endpoints
|
5
|
+
class << self
|
6
|
+
def base_url
|
7
|
+
if Tappay.configuration.sandbox?
|
8
|
+
'https://sandbox.tappaysdk.com'
|
9
|
+
else
|
10
|
+
'https://prod.tappaysdk.com'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
module CreditCard
|
16
|
+
class << self
|
17
|
+
def payment_by_prime_url
|
18
|
+
"#{Endpoints.base_url}/tpc/payment/pay-by-prime"
|
19
|
+
end
|
20
|
+
|
21
|
+
def payment_by_token_url
|
22
|
+
"#{Endpoints.base_url}/tpc/payment/pay-by-token"
|
23
|
+
end
|
24
|
+
|
25
|
+
def refund_url
|
26
|
+
"#{Endpoints.base_url}/tpc/transaction/refund"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Transaction
|
32
|
+
class << self
|
33
|
+
def query_url
|
34
|
+
"#{Endpoints.base_url}/tpc/transaction/query"
|
35
|
+
end
|
36
|
+
|
37
|
+
def trade_history_url
|
38
|
+
"#{Endpoints.base_url}/tpc/transaction/trade-history"
|
39
|
+
end
|
40
|
+
|
41
|
+
def cap_url
|
42
|
+
"#{Endpoints.base_url}/tpc/transaction/cap"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
module Bind
|
48
|
+
class << self
|
49
|
+
def bind_card_url
|
50
|
+
"#{Endpoints.base_url}/tpc/card/bind"
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove_card_url
|
54
|
+
"#{Endpoints.base_url}/tpc/card/remove"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Tappay
|
2
|
+
class Error < StandardError; end
|
3
|
+
|
4
|
+
class ConfigurationError < Error; end
|
5
|
+
class ConnectionError < Error; end
|
6
|
+
class ValidationError < Error; end
|
7
|
+
class PaymentError < Error; end
|
8
|
+
class RefundError < Error; end
|
9
|
+
class QueryError < Error; end
|
10
|
+
|
11
|
+
class APIError < Error
|
12
|
+
attr_reader :code, :message
|
13
|
+
|
14
|
+
def initialize(code, message)
|
15
|
+
@code = code
|
16
|
+
@message = message
|
17
|
+
super("TapPay API Error (#{code}): #{message}")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Tappay
|
4
|
+
module Transaction
|
5
|
+
class Query
|
6
|
+
def initialize(order_number:, records_per_page: 50, page: 0, time: nil, order_by: nil)
|
7
|
+
@order_number = order_number
|
8
|
+
@records_per_page = records_per_page
|
9
|
+
@page = page
|
10
|
+
@time = time
|
11
|
+
@order_by = order_by
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute
|
15
|
+
client = Tappay::Client.new
|
16
|
+
response = client.post(Endpoints::Transaction.query_url, request_params)
|
17
|
+
|
18
|
+
{
|
19
|
+
status: response['status'],
|
20
|
+
msg: response['msg'],
|
21
|
+
number_of_transactions: response['number_of_transactions'],
|
22
|
+
trade_records: parse_trade_records(response['trade_records'])
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def request_params
|
29
|
+
{
|
30
|
+
records_per_page: @records_per_page,
|
31
|
+
page: @page,
|
32
|
+
filters: filters,
|
33
|
+
order_by: @order_by
|
34
|
+
}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def filters
|
38
|
+
{
|
39
|
+
order_number: @order_number,
|
40
|
+
time: @time
|
41
|
+
}.compact
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_trade_records(records)
|
45
|
+
return [] unless records&.any?
|
46
|
+
|
47
|
+
records.map do |record|
|
48
|
+
{
|
49
|
+
record_status: record['record_status'],
|
50
|
+
rec_trade_id: record['rec_trade_id'],
|
51
|
+
amount: record['amount'],
|
52
|
+
currency: record['currency'],
|
53
|
+
order_number: record['order_number'],
|
54
|
+
bank_transaction_id: record['bank_transaction_id'],
|
55
|
+
auth_code: record['auth_code'],
|
56
|
+
cardholder: parse_cardholder(record['cardholder']),
|
57
|
+
merchant_id: record['merchant_id'],
|
58
|
+
transaction_time: record['transaction_time'],
|
59
|
+
tsp: record['tsp'],
|
60
|
+
card_identifier: record['card_identifier']
|
61
|
+
}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_cardholder(cardholder)
|
66
|
+
return unless cardholder
|
67
|
+
|
68
|
+
{
|
69
|
+
phone_number: cardholder['phone_number'],
|
70
|
+
name: cardholder['name'],
|
71
|
+
email: cardholder['email']
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/tappay.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "tappay/version"
|
4
|
+
require_relative "tappay/configuration"
|
5
|
+
require_relative "tappay/client"
|
6
|
+
require_relative "tappay/errors"
|
7
|
+
require_relative "tappay/card_holder"
|
8
|
+
require_relative "tappay/endpoints"
|
9
|
+
require_relative "tappay/transaction/query"
|
10
|
+
require_relative "tappay/credit_card/pay"
|
11
|
+
require_relative "tappay/credit_card/refund"
|
12
|
+
require_relative "tappay/credit_card/instalment"
|
13
|
+
|
14
|
+
module Tappay
|
15
|
+
class Error < StandardError; end
|
16
|
+
|
17
|
+
class << self
|
18
|
+
attr_accessor :configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configure
|
22
|
+
self.configuration ||= Configuration.new
|
23
|
+
yield(configuration) if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.reset
|
27
|
+
self.configuration = Configuration.new
|
28
|
+
end
|
29
|
+
end
|
data/lib/tappay_ruby.rb
ADDED
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tappay_ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Zac
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.21.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.21.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.12'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.12'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: webmock
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.19'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.19'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: vcr
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '6.2'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '6.2'
|
69
|
+
description: A Ruby library for integrating with TapPay payment services, supporting
|
70
|
+
credit card payments, refunds, and transaction queries
|
71
|
+
email:
|
72
|
+
- 579103+7a6163@users.noreply.github.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- README.md
|
78
|
+
- lib/tappay.rb
|
79
|
+
- lib/tappay/card_holder.rb
|
80
|
+
- lib/tappay/client.rb
|
81
|
+
- lib/tappay/configuration.rb
|
82
|
+
- lib/tappay/credit_card/instalment.rb
|
83
|
+
- lib/tappay/credit_card/pay.rb
|
84
|
+
- lib/tappay/credit_card/refund.rb
|
85
|
+
- lib/tappay/endpoints.rb
|
86
|
+
- lib/tappay/errors.rb
|
87
|
+
- lib/tappay/transaction/query.rb
|
88
|
+
- lib/tappay/version.rb
|
89
|
+
- lib/tappay_ruby.rb
|
90
|
+
homepage: https://github.com/7a6163/tappay
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata:
|
94
|
+
homepage_uri: https://github.com/7a6163/tappay
|
95
|
+
source_code_uri: https://github.com/7a6163/tappay
|
96
|
+
changelog_uri: https://github.com/7a6163/tappay/blob/main/CHANGELOG.md
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options: []
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: 2.7.0
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
requirements: []
|
112
|
+
rubygems_version: 3.5.22
|
113
|
+
signing_key:
|
114
|
+
specification_version: 4
|
115
|
+
summary: Ruby wrapper for TapPay payment gateway
|
116
|
+
test_files: []
|