tappay_ruby 1.0.0 → 2.0.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/LICENSE.txt +21 -0
- data/README.md +221 -10
- data/lib/tappay/apple_pay/pay.rb +3 -26
- data/lib/tappay/client.rb +28 -4
- data/lib/tappay/configuration.rb +2 -7
- data/lib/tappay/credit_card/pay.rb +1 -1
- data/lib/tappay/endpoints.rb +0 -20
- data/lib/tappay/errors.rb +0 -3
- data/lib/tappay/google_pay/pay.rb +3 -26
- data/lib/tappay/ipass_money/pay.rb +9 -0
- data/lib/tappay/jko_pay/pay.rb +2 -57
- data/lib/tappay/line_pay/pay.rb +2 -57
- data/lib/tappay/payment_base.rb +26 -5
- data/lib/tappay/prime_payment.rb +24 -0
- data/lib/tappay/redirect_payment.rb +39 -0
- data/lib/tappay/transaction/query.rb +30 -38
- data/lib/tappay/version.rb +1 -1
- data/lib/tappay.rb +3 -1
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '079c12112d7324813f5fed82b293db20a238b0232a9b08e038189b76f523b745'
|
|
4
|
+
data.tar.gz: 3a980d58f677236213f6df7cecb3ef431ee2c352725b410e7279ef6e38796014
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 29470a1418a5bd920ae3c198b8581e1b6dfda7982bcfd7587f6a68c637a41762089d9336e2de15664b3f81618d9dd0bf2f632729df4cf65d140623c1e744338a
|
|
7
|
+
data.tar.gz: 8775276b65bdb9203c1c7621ed5da4147029387849fa0f758060b3339abd4868a1ffcb31110d411c166233ca955e1173ca4eda5c34c310c42660a6a8bff5778f
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-2026 Zac
|
|
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
CHANGED
|
@@ -14,6 +14,7 @@ A Ruby library for integrating with TapPay payment services. This gem provides a
|
|
|
14
14
|
- Line Pay
|
|
15
15
|
- JKO Pay
|
|
16
16
|
- Apple Pay
|
|
17
|
+
- iPass Money
|
|
17
18
|
- Flexible merchant identification:
|
|
18
19
|
- Support for both `merchant_id` and `merchant_group_id`
|
|
19
20
|
- Automatic fallback handling
|
|
@@ -45,6 +46,49 @@ Or install it yourself as:
|
|
|
45
46
|
$ gem install tappay_ruby
|
|
46
47
|
```
|
|
47
48
|
|
|
49
|
+
## Upgrading from 1.x to 2.0
|
|
50
|
+
|
|
51
|
+
Four changes need action. Each replaces a silent wrong answer with a loud one.
|
|
52
|
+
|
|
53
|
+
**Transaction query timestamps are milliseconds.** 1.x documented seconds.
|
|
54
|
+
TapPay accepts a seconds-magnitude range but matches nothing against it, so
|
|
55
|
+
`Transaction::Query` returned an empty list for every query. Multiply by 1000:
|
|
56
|
+
|
|
57
|
+
```ruby
|
|
58
|
+
# before - always returned []
|
|
59
|
+
time: { start_time: 30.days.ago.to_i, end_time: Time.now.to_i }
|
|
60
|
+
# after
|
|
61
|
+
time: { start_time: 30.days.ago.to_i * 1000, end_time: Time.now.to_i * 1000 }
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Out-of-scale values now raise `Tappay::ValidationError` rather than returning
|
|
65
|
+
nothing.
|
|
66
|
+
|
|
67
|
+
**`Response#success?` reflects TapPay's `status`, not the HTTP code.** TapPay
|
|
68
|
+
answers a declined card with HTTP 200 and a non-zero `status`, so 1.x reported
|
|
69
|
+
failed payments as successful. If you were already checking
|
|
70
|
+
`result['status'] == 0` yourself, nothing changes; you can now use `success?`
|
|
71
|
+
instead. See [Checking the result](#checking-the-result) for what it does and
|
|
72
|
+
does not promise.
|
|
73
|
+
|
|
74
|
+
**Trade records carry every field TapPay returns.** 1.x kept 12 of roughly 32.
|
|
75
|
+
`transaction_time` and `tsp` are gone - they matched nothing in the response
|
|
76
|
+
and were always `nil`. The transaction timestamp is `time`, in milliseconds.
|
|
77
|
+
Fields such as `refunded_amount`, `is_captured`, `original_amount`,
|
|
78
|
+
`bank_result_code` and `bank_result_msg` are now available.
|
|
79
|
+
|
|
80
|
+
**Some public constants are gone.** Grep before upgrading:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
rg 'Tappay::(PaymentError|RefundError|QueryError)|api_version|Endpoints::Bind|trade_history_url|cap_url'
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
`Tappay::PaymentError`, `Tappay::RefundError` and `Tappay::QueryError` were
|
|
87
|
+
never raised by the gem, so they never caught anything - use `success?`.
|
|
88
|
+
Removing them matters because `rescue Tappay::PaymentError` now raises
|
|
89
|
+
`NameError` when the rescue clause is evaluated, which happens only once some
|
|
90
|
+
other exception is already in flight.
|
|
91
|
+
|
|
48
92
|
## Configuration
|
|
49
93
|
|
|
50
94
|
There are several ways to configure the gem:
|
|
@@ -58,6 +102,7 @@ Tappay.configure do |config|
|
|
|
58
102
|
config.merchant_id = 'YOUR_MERCHANT_ID'
|
|
59
103
|
config.jko_pay_merchant_id = 'YOUR_JKO_PAY_MERCHANT_ID' # Optional, falls back to merchant_id if not set
|
|
60
104
|
config.line_pay_merchant_id = 'YOUR_LINE_PAY_MERCHANT_ID' # Optional, falls back to merchant_id if not set
|
|
105
|
+
config.currency = 'TWD' # Optional, default for payments that do not pass :currency
|
|
61
106
|
end
|
|
62
107
|
```
|
|
63
108
|
|
|
@@ -83,6 +128,7 @@ The gem uses the following priority order when resolving merchant IDs:
|
|
|
83
128
|
2. If `merchant_group_id` is not set:
|
|
84
129
|
- For Line Pay: Uses `line_pay_merchant_id` if set, otherwise falls back to `merchant_id`
|
|
85
130
|
- For JKO Pay: Uses `jko_pay_merchant_id` if set, otherwise falls back to `merchant_id`
|
|
131
|
+
- For iPass Money: Uses `ipass_money_merchant_id` if set, otherwise falls back to `merchant_id`
|
|
86
132
|
- For Instalments: Uses `instalment_merchant_id` if set, otherwise falls back to `merchant_id`
|
|
87
133
|
- For other payment types: Uses `merchant_id`
|
|
88
134
|
|
|
@@ -101,7 +147,7 @@ result = Tappay::CreditCard::Pay.by_prime(
|
|
|
101
147
|
name: 'Test User',
|
|
102
148
|
email: 'test@example.com'
|
|
103
149
|
}
|
|
104
|
-
)
|
|
150
|
+
).execute
|
|
105
151
|
|
|
106
152
|
# Payment with saved card token
|
|
107
153
|
result = Tappay::CreditCard::Pay.by_token(
|
|
@@ -111,7 +157,7 @@ result = Tappay::CreditCard::Pay.by_token(
|
|
|
111
157
|
currency: 'TWD',
|
|
112
158
|
details: 'Order Details',
|
|
113
159
|
ccv_prime: 'ccv_prime_from_tappay' # Optional: CVV verification
|
|
114
|
-
)
|
|
160
|
+
).execute
|
|
115
161
|
|
|
116
162
|
# Instalment payment (3-30 months)
|
|
117
163
|
result = Tappay::CreditCard::Instalment.by_prime(
|
|
@@ -124,7 +170,7 @@ result = Tappay::CreditCard::Instalment.by_prime(
|
|
|
124
170
|
name: 'Test User',
|
|
125
171
|
email: 'test@example.com'
|
|
126
172
|
}
|
|
127
|
-
)
|
|
173
|
+
).execute
|
|
128
174
|
|
|
129
175
|
# Instalment payment with saved card token
|
|
130
176
|
result = Tappay::CreditCard::Instalment.by_token(
|
|
@@ -134,7 +180,7 @@ result = Tappay::CreditCard::Instalment.by_token(
|
|
|
134
180
|
instalment: 12,
|
|
135
181
|
details: 'Order Details',
|
|
136
182
|
ccv_prime: 'ccv_prime_from_tappay' # Optional: CVV verification
|
|
137
|
-
)
|
|
183
|
+
).execute
|
|
138
184
|
```
|
|
139
185
|
|
|
140
186
|
### Line Pay
|
|
@@ -206,16 +252,50 @@ payment = Tappay::JkoPay::Pay.new(payment_options)
|
|
|
206
252
|
result = payment.execute
|
|
207
253
|
```
|
|
208
254
|
|
|
255
|
+
### iPass Money
|
|
256
|
+
|
|
257
|
+
#### Configuration
|
|
258
|
+
|
|
259
|
+
```ruby
|
|
260
|
+
Tappay.configure do |config|
|
|
261
|
+
config.partner_key = 'YOUR_PARTNER_KEY'
|
|
262
|
+
config.merchant_id = 'YOUR_MERCHANT_ID'
|
|
263
|
+
config.merchant_group_id = 'YOUR_MERCHANT_GROUP_ID' # Optional, mutually exclusive with merchant_id
|
|
264
|
+
config.ipass_money_merchant_id = 'YOUR_IPASS_MONEY_MERCHANT_ID' # Optional, falls back to merchant_id if not set
|
|
265
|
+
config.sandbox = true # Set to false for production
|
|
266
|
+
end
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Processing an iPass Money Payment
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
payment_options = {
|
|
273
|
+
prime: 'ipass_money_prime',
|
|
274
|
+
amount: 1000,
|
|
275
|
+
details: 'Some item',
|
|
276
|
+
frontend_redirect_url: 'https://your-site.com/ipass_money/result',
|
|
277
|
+
backend_notify_url: 'https://your-site.com/ipass_money/notify',
|
|
278
|
+
cardholder: {
|
|
279
|
+
phone_number: '0912345678',
|
|
280
|
+
name: 'Test User',
|
|
281
|
+
email: 'test@example.com'
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
payment = Tappay::IPassMoney::Pay.new(payment_options)
|
|
286
|
+
result = payment.execute
|
|
287
|
+
```
|
|
288
|
+
|
|
209
289
|
### Transaction Query
|
|
210
290
|
|
|
211
291
|
Query transaction records with required time range:
|
|
212
292
|
|
|
213
293
|
```ruby
|
|
214
|
-
#
|
|
294
|
+
# time is required; TapPay caps the range at 90 days
|
|
215
295
|
result = Tappay::Transaction::Query.new(
|
|
216
296
|
time: {
|
|
217
|
-
start_time:
|
|
218
|
-
end_time:
|
|
297
|
+
start_time: 1706198400000, # Unix timestamp in MILLISECONDS
|
|
298
|
+
end_time: 1706284800000 # Unix timestamp in MILLISECONDS
|
|
219
299
|
},
|
|
220
300
|
order_number: 'ORDER123', # Optional: filter by order number
|
|
221
301
|
records_per_page: 50, # Optional: default is 50
|
|
@@ -234,7 +314,65 @@ result[:trade_records].each do |record|
|
|
|
234
314
|
end
|
|
235
315
|
```
|
|
236
316
|
|
|
237
|
-
|
|
317
|
+
Every field TapPay returns for a trade record is passed through verbatim, with
|
|
318
|
+
keys symbolized (recursively, so nested objects like `refund_info` and
|
|
319
|
+
`card_info` are symbol-keyed too). The gem does not whitelist fields, so
|
|
320
|
+
`refunded_amount`, `is_captured`, `bank_result_code`, `bank_result_msg`,
|
|
321
|
+
`original_amount`, `time` and the various `*_millis` timestamps are all
|
|
322
|
+
available, and fields TapPay adds later work without a gem upgrade. The exact
|
|
323
|
+
key set varies by payment method. See
|
|
324
|
+
`spec/fixtures/transaction_query_response.json` for a real response.
|
|
325
|
+
|
|
326
|
+
Two things worth knowing about the records:
|
|
327
|
+
|
|
328
|
+
- `amount` is what remains after refunds. A fully refunded transaction reports
|
|
329
|
+
`amount: 0` with `original_amount` and `refunded_amount` both set to the
|
|
330
|
+
original charge, so compare `refunded_amount` against `original_amount` to
|
|
331
|
+
tell a partial refund from a full one.
|
|
332
|
+
- The transaction timestamp is `time` (milliseconds). `transaction_complete_millis`
|
|
333
|
+
is `0` on records that have not completed.
|
|
334
|
+
|
|
335
|
+
`status` in the result is TapPay's, not an HTTP code, and **`2` means "End of
|
|
336
|
+
list", not "nothing found"** - it is what you get on the last page, records
|
|
337
|
+
included. Verified against the sandbox: a query returning 33 records answered
|
|
338
|
+
`status: 2`. Treat `0` and `2` alike and read `trade_records`; checking
|
|
339
|
+
`status == 0` before looking drops the final page. `Response#success?` is
|
|
340
|
+
`status == 0`, so it is the wrong question for a query - `Transaction::Query`
|
|
341
|
+
does not use it.
|
|
342
|
+
|
|
343
|
+
Note: `time` is required and its timestamps are in **milliseconds**, not
|
|
344
|
+
seconds. Seconds are accepted by TapPay but match nothing, so the gem rejects
|
|
345
|
+
them with a `ValidationError` rather than returning an empty list. TapPay caps
|
|
346
|
+
the range at 90 days.
|
|
347
|
+
|
|
348
|
+
### Checking the result
|
|
349
|
+
|
|
350
|
+
`Pay.by_prime` and friends return an unexecuted payment object - call
|
|
351
|
+
`execute` to send the request. TapPay reports business failures (declined
|
|
352
|
+
card, insufficient funds, expired card) as **HTTP 200 with a non-zero
|
|
353
|
+
`status`**, so `success?` checks that status rather than the HTTP code:
|
|
354
|
+
|
|
355
|
+
```ruby
|
|
356
|
+
result = Tappay::CreditCard::Pay.by_prime(...).execute
|
|
357
|
+
|
|
358
|
+
if result.success? # status == 0
|
|
359
|
+
result['rec_trade_id']
|
|
360
|
+
else
|
|
361
|
+
result['status'] # e.g. 10003
|
|
362
|
+
result['msg'] # e.g. 'Card is declined'
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
**`success?` does not mean the money has moved.** It means TapPay accepted and
|
|
367
|
+
processed the request. What that implies depends on the payment method:
|
|
368
|
+
|
|
369
|
+
- **Credit card** - the transaction was authorised. Capture is asynchronous;
|
|
370
|
+
confirm it with `is_captured` from a `Transaction::Query`.
|
|
371
|
+
- **LINE Pay / JKO Pay / iPass Money** - only that a `payment_url` was created.
|
|
372
|
+
The customer has not paid yet. Redirect them to `result['payment_url']`, and
|
|
373
|
+
treat your `backend_notify_url` callback plus a `Transaction::Query` as the
|
|
374
|
+
authoritative answer. TapPay's own guidance is to query the Record API before
|
|
375
|
+
showing a result page to the customer.
|
|
238
376
|
|
|
239
377
|
### Error Handling
|
|
240
378
|
|
|
@@ -245,8 +383,12 @@ begin
|
|
|
245
383
|
result = Tappay::CreditCard::Pay.by_prime(
|
|
246
384
|
prime: 'prime_from_tappay_sdk',
|
|
247
385
|
amount: 100,
|
|
386
|
+
details: 'Order Details',
|
|
248
387
|
order_number: 'ORDER-123'
|
|
249
|
-
)
|
|
388
|
+
).execute
|
|
389
|
+
|
|
390
|
+
# TapPay reports declined cards as HTTP 200 with a non-zero status.
|
|
391
|
+
raise "Payment failed: #{result['msg']}" unless result.success?
|
|
250
392
|
rescue Tappay::ValidationError => e
|
|
251
393
|
# Handle validation errors (e.g., missing required fields)
|
|
252
394
|
puts "Validation error: #{e.message}"
|
|
@@ -256,9 +398,78 @@ rescue Tappay::Error => e
|
|
|
256
398
|
end
|
|
257
399
|
```
|
|
258
400
|
|
|
401
|
+
The gem raises `Tappay::ValidationError` (bad or missing options),
|
|
402
|
+
`Tappay::ConfigurationError` (authentication rejected) and
|
|
403
|
+
`Tappay::ConnectionError` (timeout, unreachable endpoint, unparseable
|
|
404
|
+
response), all of which inherit from `Tappay::Error`. Business failures are
|
|
405
|
+
not exceptions - check `success?`.
|
|
406
|
+
|
|
259
407
|
## Development
|
|
260
408
|
|
|
261
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then
|
|
409
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then run
|
|
410
|
+
`bundle exec rspec` for the tests, or `bin/console` for an interactive prompt.
|
|
411
|
+
|
|
412
|
+
### Mutation testing
|
|
413
|
+
|
|
414
|
+
Line coverage says a line ran, not that anything checked what it did. Both bugs
|
|
415
|
+
fixed in 2.0.0 - a field name that never matched the API and a time filter in
|
|
416
|
+
the wrong unit - sat under 100% line coverage for the life of the gem.
|
|
417
|
+
|
|
418
|
+
[Mutant](https://github.com/mbj/mutant) changes the source in small ways (flips
|
|
419
|
+
a boolean, drops a call, swaps an operator) and reruns the suite. A mutation
|
|
420
|
+
that survives marks behaviour nothing asserts.
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
bundle config set --local with mutant
|
|
424
|
+
bundle install
|
|
425
|
+
bundle exec mutant run
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
It needs Ruby >= 3.3, so it lives in an optional bundle group rather than the
|
|
429
|
+
gemspec - the gem itself supports >= 2.7. Scope is `config/mutant.yml`:
|
|
430
|
+
`Transaction::Query`, `Response`, and the two payment base classes that five
|
|
431
|
+
payment methods now share.
|
|
432
|
+
|
|
433
|
+
The current score is 95.34% (676 of 709). Mutant exits non-zero whenever
|
|
434
|
+
anything survives, and the 33 survivors here are equivalent mutations that no
|
|
435
|
+
test can distinguish - inside `module Tappay`, `Client.new` and
|
|
436
|
+
`Tappay::Client.new` are the same call, and `is_a?(Hash)` and
|
|
437
|
+
`instance_of?(Hash)` differ only for a Hash subclass nothing passes. Each one
|
|
438
|
+
is listed in `config/mutant.yml` with its reason, and CI gates on the score
|
|
439
|
+
rather than the exit code.
|
|
440
|
+
|
|
441
|
+
If the score drops, an assertion went missing. Read the new survivor rather
|
|
442
|
+
than lowering the floor: the first run here scored 77.90%, and every one of
|
|
443
|
+
those gaps was genuine - including two tests that passed whether or not the
|
|
444
|
+
code they covered was there at all.
|
|
445
|
+
|
|
446
|
+
### Contract tests against the sandbox
|
|
447
|
+
|
|
448
|
+
Stubbed tests assert the gem against its own assumptions, so they cannot catch
|
|
449
|
+
the gem being wrong about TapPay. Every bug fixed in 2.0.0 was exactly that -
|
|
450
|
+
a field name that did not exist, a time filter in the wrong unit - and all of
|
|
451
|
+
them survived a suite with 100% line coverage. One real call catches the lot.
|
|
452
|
+
|
|
453
|
+
```bash
|
|
454
|
+
export TAPPAY_SANDBOX_PARTNER_KEY=...
|
|
455
|
+
bundle exec rspec spec/contract --tag contract
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The specs are excluded from the default run and skip with an explanation if
|
|
459
|
+
the key is unset, so a clone without credentials behaves normally. Nothing is
|
|
460
|
+
written: the Record API is read-only and needs only a partner key.
|
|
461
|
+
|
|
462
|
+
They check that a millisecond window is accepted, that every field
|
|
463
|
+
`spec/fixtures/transaction_query_response.json` claims still exists in the live
|
|
464
|
+
response, that the transaction timestamp is `time` in milliseconds, and that
|
|
465
|
+
the amounts and capture state refunds reconcile against are all present. New
|
|
466
|
+
fields TapPay has added are reported rather than failed - the gem passes
|
|
467
|
+
everything through, so they break nothing; the fixture just wants re-capturing.
|
|
468
|
+
|
|
469
|
+
Only `Transaction::Query` is covered. Charging needs a `prime`, which the
|
|
470
|
+
frontend SDK mints from a test card - it is single-use, expires in seconds, and
|
|
471
|
+
there is no server-side way to obtain one, so payment and refund flows cannot
|
|
472
|
+
be driven from a test suite without automating a browser.
|
|
262
473
|
|
|
263
474
|
## Contributing
|
|
264
475
|
|
data/lib/tappay/apple_pay/pay.rb
CHANGED
|
@@ -1,32 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Tappay
|
|
4
4
|
module ApplePay
|
|
5
|
-
class Pay <
|
|
6
|
-
|
|
7
|
-
def endpoint_url
|
|
8
|
-
Tappay::Endpoints::Payment.pay_by_prime_url
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
def get_merchant_id
|
|
14
|
-
return nil if Tappay.configuration.merchant_group_id
|
|
15
|
-
|
|
16
|
-
Tappay.configuration.apple_pay_merchant_id || super
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def additional_required_options
|
|
20
|
-
[:prime, :cardholder]
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
protected
|
|
24
|
-
|
|
25
|
-
def payment_data
|
|
26
|
-
super.merge(
|
|
27
|
-
prime: options[:prime]
|
|
28
|
-
)
|
|
29
|
-
end
|
|
5
|
+
class Pay < PrimePayment
|
|
6
|
+
uses_merchant_id :apple_pay_merchant_id
|
|
30
7
|
end
|
|
31
8
|
end
|
|
32
9
|
end
|
data/lib/tappay/client.rb
CHANGED
|
@@ -62,24 +62,48 @@ module Tappay
|
|
|
62
62
|
attr_reader :code, :body, :headers
|
|
63
63
|
|
|
64
64
|
def initialize(net_http_response)
|
|
65
|
-
@response = net_http_response
|
|
66
65
|
@code = net_http_response.code.to_i
|
|
67
66
|
@body = net_http_response.body
|
|
68
67
|
@headers = net_http_response.to_hash
|
|
69
68
|
end
|
|
70
69
|
|
|
70
|
+
# Every TapPay endpoint answers with a JSON object, so a body that is not
|
|
71
|
+
# one means something other than TapPay replied - a maintenance page, a
|
|
72
|
+
# proxy, a WAF. Returning the raw body instead turned
|
|
73
|
+
# `parsed_response['status']` into a String#[] substring search, which
|
|
74
|
+
# answers nil without complaining. Raise rather than hand back something
|
|
75
|
+
# that reads like a result.
|
|
71
76
|
def parsed_response
|
|
72
|
-
@parsed_response ||= JSON.parse(@body)
|
|
77
|
+
@parsed_response ||= JSON.parse(@body.to_s).then do |parsed|
|
|
78
|
+
raise ConnectionError, unexpected_body unless parsed.is_a?(Hash)
|
|
79
|
+
|
|
80
|
+
parsed
|
|
81
|
+
end
|
|
73
82
|
rescue JSON::ParserError
|
|
74
|
-
|
|
83
|
+
raise ConnectionError, unexpected_body
|
|
75
84
|
end
|
|
76
85
|
|
|
86
|
+
# TapPay signals business failures (declined card, insufficient funds,
|
|
87
|
+
# expired card) with HTTP 200 and a non-zero `status`, so the HTTP code
|
|
88
|
+
# alone says nothing. Client only builds a Response once validate_response
|
|
89
|
+
# has accepted the HTTP code, so `status` is the only question left.
|
|
90
|
+
#
|
|
91
|
+
# This means "TapPay processed the request", not "the money moved": for a
|
|
92
|
+
# credit card the transaction is authorised but capture is asynchronous,
|
|
93
|
+
# and for LINE Pay / JKO Pay / iPass Money it means only that a payment_url
|
|
94
|
+
# was created and the customer has yet to pay. Confirm with Transaction::Query.
|
|
77
95
|
def success?
|
|
78
|
-
|
|
96
|
+
parsed_response['status'] == 0
|
|
79
97
|
end
|
|
80
98
|
|
|
81
99
|
def [](key)
|
|
82
100
|
parsed_response[key]
|
|
83
101
|
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
def unexpected_body
|
|
106
|
+
"Expected a JSON object from TapPay, got: #{@body.to_s[0, 200]}"
|
|
107
|
+
end
|
|
84
108
|
end
|
|
85
109
|
end
|
data/lib/tappay/configuration.rb
CHANGED
|
@@ -4,16 +4,11 @@ module Tappay
|
|
|
4
4
|
class Configuration
|
|
5
5
|
attr_accessor :partner_key, :merchant_id, :merchant_group_id, :instalment_merchant_id,
|
|
6
6
|
:line_pay_merchant_id, :jko_pay_merchant_id, :currency,
|
|
7
|
-
:google_pay_merchant_id, :apple_pay_merchant_id
|
|
8
|
-
|
|
7
|
+
:google_pay_merchant_id, :apple_pay_merchant_id,
|
|
8
|
+
:ipass_money_merchant_id
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
11
11
|
@mode = :sandbox
|
|
12
|
-
@api_version = '3'
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def api_version
|
|
16
|
-
@api_version.to_s
|
|
17
12
|
end
|
|
18
13
|
|
|
19
14
|
def sandbox?
|
data/lib/tappay/endpoints.rb
CHANGED
|
@@ -33,26 +33,6 @@ module Tappay
|
|
|
33
33
|
def query_url
|
|
34
34
|
"#{Endpoints.base_url}/tpc/transaction/query"
|
|
35
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
36
|
end
|
|
57
37
|
end
|
|
58
38
|
end
|
data/lib/tappay/errors.rb
CHANGED
|
@@ -1,32 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Tappay
|
|
4
4
|
module GooglePay
|
|
5
|
-
class Pay <
|
|
6
|
-
|
|
7
|
-
def endpoint_url
|
|
8
|
-
Tappay::Endpoints::Payment.pay_by_prime_url
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
private
|
|
12
|
-
|
|
13
|
-
def get_merchant_id
|
|
14
|
-
return nil if Tappay.configuration.merchant_group_id
|
|
15
|
-
|
|
16
|
-
Tappay.configuration.google_pay_merchant_id || super
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def additional_required_options
|
|
20
|
-
[:prime, :cardholder]
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
protected
|
|
24
|
-
|
|
25
|
-
def payment_data
|
|
26
|
-
super.merge(
|
|
27
|
-
prime: options[:prime]
|
|
28
|
-
)
|
|
29
|
-
end
|
|
5
|
+
class Pay < PrimePayment
|
|
6
|
+
uses_merchant_id :google_pay_merchant_id
|
|
30
7
|
end
|
|
31
8
|
end
|
|
32
9
|
end
|
data/lib/tappay/jko_pay/pay.rb
CHANGED
|
@@ -2,63 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Tappay
|
|
4
4
|
module JkoPay
|
|
5
|
-
class Pay <
|
|
6
|
-
|
|
7
|
-
Tappay::Endpoints::Payment.pay_by_prime_url
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def get_merchant_id
|
|
13
|
-
# If merchant_group_id is set, it takes precedence
|
|
14
|
-
return nil if Tappay.configuration.merchant_group_id
|
|
15
|
-
|
|
16
|
-
# Otherwise, use jko_pay_merchant_id or fall back to default merchant_id
|
|
17
|
-
Tappay.configuration.jko_pay_merchant_id || super
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def additional_required_options
|
|
21
|
-
[:prime, :frontend_redirect_url, :backend_notify_url, :cardholder]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def validate_options!
|
|
25
|
-
super
|
|
26
|
-
validate_result_url_format!
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def validate_result_url_format!
|
|
30
|
-
# First validate that if result_url is provided, it's a hash with required fields
|
|
31
|
-
if options.key?(:result_url)
|
|
32
|
-
raise ValidationError, "result_url must be a hash" unless options[:result_url].is_a?(Hash)
|
|
33
|
-
|
|
34
|
-
result_url = options[:result_url]
|
|
35
|
-
required_fields = %w[frontend_redirect_url backend_notify_url]
|
|
36
|
-
missing = required_fields.select { |field| result_url[field.to_sym].nil? && result_url[field].nil? }
|
|
37
|
-
|
|
38
|
-
if missing.any?
|
|
39
|
-
raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Then validate frontend_redirect_url and backend_notify_url are present and not empty
|
|
44
|
-
if !options[:frontend_redirect_url].to_s.strip.empty? && !options[:backend_notify_url].to_s.strip.empty?
|
|
45
|
-
return
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
protected
|
|
52
|
-
|
|
53
|
-
def payment_data
|
|
54
|
-
super.merge(
|
|
55
|
-
prime: options[:prime],
|
|
56
|
-
result_url: {
|
|
57
|
-
frontend_redirect_url: options[:frontend_redirect_url],
|
|
58
|
-
backend_notify_url: options[:backend_notify_url]
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
end
|
|
5
|
+
class Pay < RedirectPayment
|
|
6
|
+
uses_merchant_id :jko_pay_merchant_id
|
|
62
7
|
end
|
|
63
8
|
end
|
|
64
9
|
end
|
data/lib/tappay/line_pay/pay.rb
CHANGED
|
@@ -2,63 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Tappay
|
|
4
4
|
module LinePay
|
|
5
|
-
class Pay <
|
|
6
|
-
|
|
7
|
-
Tappay::Endpoints::Payment.pay_by_prime_url
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
private
|
|
11
|
-
|
|
12
|
-
def get_merchant_id
|
|
13
|
-
# If merchant_group_id is set, it takes precedence
|
|
14
|
-
return nil if Tappay.configuration.merchant_group_id
|
|
15
|
-
|
|
16
|
-
# Otherwise, use line_pay_merchant_id or fall back to default merchant_id
|
|
17
|
-
Tappay.configuration.line_pay_merchant_id || super
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def additional_required_options
|
|
21
|
-
[:prime, :frontend_redirect_url, :backend_notify_url, :cardholder]
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def validate_options!
|
|
25
|
-
super
|
|
26
|
-
validate_result_url_format!
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def validate_result_url_format!
|
|
30
|
-
# First validate that if result_url is provided, it's a hash with required fields
|
|
31
|
-
if options.key?(:result_url)
|
|
32
|
-
raise ValidationError, "result_url must be a hash" unless options[:result_url].is_a?(Hash)
|
|
33
|
-
|
|
34
|
-
result_url = options[:result_url]
|
|
35
|
-
required_fields = %w[frontend_redirect_url backend_notify_url]
|
|
36
|
-
missing = required_fields.select { |field| result_url[field.to_sym].nil? && result_url[field].nil? }
|
|
37
|
-
|
|
38
|
-
if missing.any?
|
|
39
|
-
raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Then validate frontend_redirect_url and backend_notify_url are present and not empty
|
|
44
|
-
if !options[:frontend_redirect_url].to_s.strip.empty? && !options[:backend_notify_url].to_s.strip.empty?
|
|
45
|
-
return
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
protected
|
|
52
|
-
|
|
53
|
-
def payment_data
|
|
54
|
-
super.merge(
|
|
55
|
-
prime: options[:prime],
|
|
56
|
-
result_url: {
|
|
57
|
-
frontend_redirect_url: options[:frontend_redirect_url],
|
|
58
|
-
backend_notify_url: options[:backend_notify_url]
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
end
|
|
5
|
+
class Pay < RedirectPayment
|
|
6
|
+
uses_merchant_id :line_pay_merchant_id
|
|
62
7
|
end
|
|
63
8
|
end
|
|
64
9
|
end
|
data/lib/tappay/payment_base.rb
CHANGED
|
@@ -4,6 +4,22 @@ module Tappay
|
|
|
4
4
|
class PaymentBase < Client
|
|
5
5
|
VALID_INSTALMENT_VALUES = [0, 3, 6, 12, 18, 24, 30].freeze
|
|
6
6
|
|
|
7
|
+
class << self
|
|
8
|
+
# Some payment methods get their own merchant ID from TapPay. Declaring
|
|
9
|
+
# the config key here replaces a get_merchant_id override per class.
|
|
10
|
+
def uses_merchant_id(key)
|
|
11
|
+
@merchant_id_key = key
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Walks the ancestry, because a class-level ivar is not inherited and
|
|
15
|
+
# the get_merchant_id overrides this replaced were. Without this, a
|
|
16
|
+
# subclass of a payment class silently charges the default merchant.
|
|
17
|
+
def merchant_id_key
|
|
18
|
+
@merchant_id_key ||
|
|
19
|
+
(superclass.merchant_id_key if superclass.respond_to?(:merchant_id_key))
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
7
23
|
def initialize(options = {})
|
|
8
24
|
super
|
|
9
25
|
validate_options!
|
|
@@ -36,7 +52,7 @@ module Tappay
|
|
|
36
52
|
partner_key: Tappay.configuration.partner_key,
|
|
37
53
|
amount: options[:amount],
|
|
38
54
|
details: options[:details],
|
|
39
|
-
currency: options[:currency] || 'TWD',
|
|
55
|
+
currency: options[:currency] || Tappay.configuration.currency || 'TWD',
|
|
40
56
|
order_number: options[:order_number],
|
|
41
57
|
three_domain_secure: options[:three_domain_secure] || false
|
|
42
58
|
}).tap do |data|
|
|
@@ -82,11 +98,11 @@ module Tappay
|
|
|
82
98
|
end
|
|
83
99
|
|
|
84
100
|
def get_merchant_id
|
|
85
|
-
#
|
|
101
|
+
# merchant_group_id takes precedence over every merchant ID.
|
|
86
102
|
return nil if Tappay.configuration.merchant_group_id
|
|
87
103
|
|
|
88
|
-
|
|
89
|
-
Tappay.configuration.merchant_id
|
|
104
|
+
key = self.class.merchant_id_key
|
|
105
|
+
(key && Tappay.configuration.public_send(key)) || Tappay.configuration.merchant_id
|
|
90
106
|
end
|
|
91
107
|
|
|
92
108
|
def base_required_options
|
|
@@ -105,7 +121,12 @@ module Tappay
|
|
|
105
121
|
end
|
|
106
122
|
|
|
107
123
|
def validate_result_url!
|
|
108
|
-
|
|
124
|
+
validate_result_url_hash!(options[:result_url])
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# The one definition of what a result_url hash has to look like. Both the
|
|
128
|
+
# 3DS path above and the redirect payment methods check against this.
|
|
129
|
+
def validate_result_url_hash!(result_url)
|
|
109
130
|
raise ValidationError, "result_url must be a hash" unless result_url.is_a?(Hash)
|
|
110
131
|
|
|
111
132
|
required_fields = %w[frontend_redirect_url backend_notify_url]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tappay
|
|
4
|
+
# Payment methods charged with a prime from TapPay's SDK, where TapPay also
|
|
5
|
+
# requires cardholder details. Apple Pay and Google Pay are this and nothing
|
|
6
|
+
# more; the redirect methods add a result_url on top.
|
|
7
|
+
class PrimePayment < PaymentBase
|
|
8
|
+
def endpoint_url
|
|
9
|
+
Tappay::Endpoints::Payment.pay_by_prime_url
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
protected
|
|
13
|
+
|
|
14
|
+
def payment_data
|
|
15
|
+
super.merge(prime: options[:prime])
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def additional_required_options
|
|
21
|
+
[:prime, :cardholder]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tappay
|
|
4
|
+
# Payment methods where TapPay answers with a payment_url and the customer
|
|
5
|
+
# finishes paying on the provider's own page: LINE Pay, JKO Pay, iPass Money.
|
|
6
|
+
# A status of 0 here means the URL was created, not that anyone has paid.
|
|
7
|
+
class RedirectPayment < PrimePayment
|
|
8
|
+
protected
|
|
9
|
+
|
|
10
|
+
def payment_data
|
|
11
|
+
super.merge(
|
|
12
|
+
result_url: {
|
|
13
|
+
frontend_redirect_url: options[:frontend_redirect_url],
|
|
14
|
+
backend_notify_url: options[:backend_notify_url]
|
|
15
|
+
}
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def additional_required_options
|
|
22
|
+
super + [:frontend_redirect_url, :backend_notify_url]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def validate_options!
|
|
26
|
+
super
|
|
27
|
+
validate_redirect_urls!
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def validate_redirect_urls!
|
|
31
|
+
validate_result_url_hash!(options[:result_url]) if options.key?(:result_url)
|
|
32
|
+
|
|
33
|
+
return unless options[:frontend_redirect_url].to_s.strip.empty? ||
|
|
34
|
+
options[:backend_notify_url].to_s.strip.empty?
|
|
35
|
+
|
|
36
|
+
raise ValidationError, "result_url must contain both frontend_redirect_url and backend_notify_url"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
module Tappay
|
|
4
4
|
module Transaction
|
|
5
5
|
class Query
|
|
6
|
+
# TapPay's time filter is in milliseconds. Values at the wrong scale are
|
|
7
|
+
# accepted by the API but match nothing (seconds land in 1970,
|
|
8
|
+
# microseconds in the year 50000), so reject them loudly rather than
|
|
9
|
+
# returning an empty list.
|
|
10
|
+
# ponytail: magnitude heuristic, only valid for 1973-03-03 .. 5138-11-16
|
|
11
|
+
MILLIS_FLOOR = 100_000_000_000
|
|
12
|
+
MILLIS_CEILING = 100_000_000_000_000
|
|
13
|
+
|
|
6
14
|
def initialize(time:, order_number: nil, bank_transaction_id: nil, records_per_page: 50, page: 0, order_by: nil)
|
|
7
15
|
@time = validate_time!(time)
|
|
8
16
|
@order_number = order_number
|
|
@@ -16,15 +24,9 @@ module Tappay
|
|
|
16
24
|
client = Tappay::Client.new
|
|
17
25
|
response = client.post(Endpoints::Transaction.query_url, request_params)
|
|
18
26
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
records_per_page: response['records_per_page'],
|
|
23
|
-
page: response['page'],
|
|
24
|
-
total_page_count: response['total_page_count'],
|
|
25
|
-
number_of_transactions: response['number_of_transactions'],
|
|
26
|
-
trade_records: parse_trade_records(response['trade_records'])
|
|
27
|
-
}
|
|
27
|
+
result = symbolize_keys(response.parsed_response)
|
|
28
|
+
result[:trade_records] ||= []
|
|
29
|
+
result
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
private
|
|
@@ -53,7 +55,17 @@ module Tappay
|
|
|
53
55
|
end
|
|
54
56
|
|
|
55
57
|
unless time[:start_time].is_a?(Integer) && time[:end_time].is_a?(Integer)
|
|
56
|
-
raise Tappay::ValidationError, "start_time and end_time must be Unix timestamps (integers)"
|
|
58
|
+
raise Tappay::ValidationError, "start_time and end_time must be Unix timestamps in milliseconds (integers)"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
if time[:start_time] < MILLIS_FLOOR || time[:end_time] < MILLIS_FLOOR
|
|
62
|
+
raise Tappay::ValidationError,
|
|
63
|
+
"start_time and end_time must be in milliseconds, not seconds (multiply by 1000)"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
if time[:start_time] >= MILLIS_CEILING || time[:end_time] >= MILLIS_CEILING
|
|
67
|
+
raise Tappay::ValidationError,
|
|
68
|
+
"start_time and end_time are too large to be milliseconds (microseconds?)"
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
if time[:start_time] > time[:end_time]
|
|
@@ -63,36 +75,16 @@ module Tappay
|
|
|
63
75
|
time
|
|
64
76
|
end
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
currency: record['currency'],
|
|
75
|
-
order_number: record['order_number'],
|
|
76
|
-
bank_transaction_id: record['bank_transaction_id'],
|
|
77
|
-
auth_code: record['auth_code'],
|
|
78
|
-
cardholder: parse_cardholder(record['cardholder']),
|
|
79
|
-
merchant_id: record['merchant_id'],
|
|
80
|
-
transaction_time: record['transaction_time'],
|
|
81
|
-
tsp: record['tsp'],
|
|
82
|
-
card_identifier: record['card_identifier']
|
|
83
|
-
}
|
|
78
|
+
# Pass every field TapPay returns straight through, at the envelope level
|
|
79
|
+
# as well as per trade record. A whitelist silently drops new fields and
|
|
80
|
+
# silently returns nil for a mistyped key.
|
|
81
|
+
def symbolize_keys(value)
|
|
82
|
+
case value
|
|
83
|
+
when Hash then value.to_h { |k, v| [k.to_sym, symbolize_keys(v)] }
|
|
84
|
+
when Array then value.map { |v| symbolize_keys(v) }
|
|
85
|
+
else value
|
|
84
86
|
end
|
|
85
87
|
end
|
|
86
|
-
|
|
87
|
-
def parse_cardholder(cardholder)
|
|
88
|
-
return unless cardholder
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
phone_number: cardholder['phone_number'],
|
|
92
|
-
name: cardholder['name'],
|
|
93
|
-
email: cardholder['email']
|
|
94
|
-
}
|
|
95
|
-
end
|
|
96
88
|
end
|
|
97
89
|
end
|
|
98
90
|
end
|
data/lib/tappay/version.rb
CHANGED
data/lib/tappay.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'csv'
|
|
4
3
|
require 'json'
|
|
5
4
|
require 'net/http'
|
|
6
5
|
require 'uri'
|
|
@@ -11,6 +10,8 @@ require_relative "tappay/client"
|
|
|
11
10
|
require_relative "tappay/errors"
|
|
12
11
|
require_relative "tappay/refund"
|
|
13
12
|
require_relative "tappay/payment_base"
|
|
13
|
+
require_relative "tappay/prime_payment"
|
|
14
|
+
require_relative "tappay/redirect_payment"
|
|
14
15
|
require_relative "tappay/card_holder"
|
|
15
16
|
require_relative "tappay/endpoints"
|
|
16
17
|
require_relative "tappay/transaction/query"
|
|
@@ -20,6 +21,7 @@ require_relative "tappay/line_pay/pay"
|
|
|
20
21
|
require_relative "tappay/jko_pay/pay"
|
|
21
22
|
require_relative "tappay/apple_pay/pay"
|
|
22
23
|
require_relative "tappay/google_pay/pay"
|
|
24
|
+
require_relative "tappay/ipass_money/pay"
|
|
23
25
|
|
|
24
26
|
module Tappay
|
|
25
27
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tappay_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zac
|
|
@@ -57,14 +57,14 @@ dependencies:
|
|
|
57
57
|
requirements:
|
|
58
58
|
- - "~>"
|
|
59
59
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
60
|
+
version: '3.1'
|
|
61
61
|
type: :development
|
|
62
62
|
prerelease: false
|
|
63
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
64
|
requirements:
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
67
|
+
version: '3.1'
|
|
68
68
|
- !ruby/object:Gem::Dependency
|
|
69
69
|
name: webmock
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -115,6 +115,7 @@ executables: []
|
|
|
115
115
|
extensions: []
|
|
116
116
|
extra_rdoc_files: []
|
|
117
117
|
files:
|
|
118
|
+
- LICENSE.txt
|
|
118
119
|
- README.md
|
|
119
120
|
- lib/tappay.rb
|
|
120
121
|
- lib/tappay/apple_pay/pay.rb
|
|
@@ -126,9 +127,12 @@ files:
|
|
|
126
127
|
- lib/tappay/endpoints.rb
|
|
127
128
|
- lib/tappay/errors.rb
|
|
128
129
|
- lib/tappay/google_pay/pay.rb
|
|
130
|
+
- lib/tappay/ipass_money/pay.rb
|
|
129
131
|
- lib/tappay/jko_pay/pay.rb
|
|
130
132
|
- lib/tappay/line_pay/pay.rb
|
|
131
133
|
- lib/tappay/payment_base.rb
|
|
134
|
+
- lib/tappay/prime_payment.rb
|
|
135
|
+
- lib/tappay/redirect_payment.rb
|
|
132
136
|
- lib/tappay/refund.rb
|
|
133
137
|
- lib/tappay/transaction/query.rb
|
|
134
138
|
- lib/tappay/version.rb
|
|
@@ -154,7 +158,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
154
158
|
- !ruby/object:Gem::Version
|
|
155
159
|
version: '0'
|
|
156
160
|
requirements: []
|
|
157
|
-
rubygems_version: 4.0.
|
|
161
|
+
rubygems_version: 4.0.16
|
|
158
162
|
specification_version: 4
|
|
159
163
|
summary: Ruby wrapper for TapPay payment gateway
|
|
160
164
|
test_files: []
|