paygate_pk 0.2.3 → 1.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +34 -3
  3. data/CHANGELOG.md +213 -0
  4. data/Gemfile.lock +46 -5
  5. data/README.md +291 -64
  6. data/lib/paygate_pk/coercions.rb +64 -0
  7. data/lib/paygate_pk/config.rb +113 -25
  8. data/lib/paygate_pk/contracts/access_token.rb +8 -2
  9. data/lib/paygate_pk/contracts/callback_event.rb +38 -0
  10. data/lib/paygate_pk/contracts/charge_result.rb +52 -0
  11. data/lib/paygate_pk/contracts/inquiry_result.rb +58 -0
  12. data/lib/paygate_pk/contracts/redirect_request.rb +30 -0
  13. data/lib/paygate_pk/easy_paisa/client.rb +64 -0
  14. data/lib/paygate_pk/easy_paisa/endpoints.rb +34 -0
  15. data/lib/paygate_pk/easy_paisa/inquiry.rb +87 -0
  16. data/lib/paygate_pk/easy_paisa/mobile_account.rb +123 -0
  17. data/lib/paygate_pk/easy_paisa/otc.rb +146 -0
  18. data/lib/paygate_pk/easy_paisa.rb +21 -0
  19. data/lib/paygate_pk/errors.rb +16 -3
  20. data/lib/paygate_pk/http/client.rb +84 -71
  21. data/lib/paygate_pk/pay_fast/auth.rb +79 -0
  22. data/lib/paygate_pk/pay_fast/callback.rb +92 -0
  23. data/lib/paygate_pk/pay_fast/endpoints.rb +38 -0
  24. data/lib/paygate_pk/pay_fast/redirect.rb +227 -0
  25. data/lib/paygate_pk/pay_fast.rb +19 -0
  26. data/lib/paygate_pk/rails/railtie.rb +19 -0
  27. data/lib/paygate_pk/rails/view_helpers.rb +159 -0
  28. data/lib/paygate_pk/util/credentials.rb +27 -0
  29. data/lib/paygate_pk/util/signature/pay_fast.rb +25 -0
  30. data/lib/paygate_pk/version.rb +1 -1
  31. data/lib/paygate_pk.rb +54 -18
  32. metadata +34 -32
  33. data/lib/paygate_pk/contracts/bearer_token.rb +0 -18
  34. data/lib/paygate_pk/contracts/hosted_checkout.rb +0 -8
  35. data/lib/paygate_pk/contracts/instrument.rb +0 -10
  36. data/lib/paygate_pk/contracts/webhook_event.rb +0 -24
  37. data/lib/paygate_pk/providers/pay_fast/auth.rb +0 -61
  38. data/lib/paygate_pk/providers/pay_fast/checkout.rb +0 -157
  39. data/lib/paygate_pk/providers/pay_fast/client.rb +0 -53
  40. data/lib/paygate_pk/providers/pay_fast/tokenization/instrument.rb +0 -63
  41. data/lib/paygate_pk/providers/pay_fast/tokenization/token.rb +0 -65
  42. data/lib/paygate_pk/providers/pay_fast/webhook.rb +0 -74
  43. data/lib/paygate_pk/util/html.rb +0 -42
  44. data/lib/paygate_pk/util/signature.rb +0 -18
  45. data/paygate_pk.gemspec +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9dafac109de3aec799f0269fdd9f1d8ccd0990b0f435ed293d98b6b2c8c309ec
4
- data.tar.gz: 04c5136a8bd15c6a24d7deb399e514aeb823423497ddeca8068512eb292ae47c
3
+ metadata.gz: dd62d57ff115a50c39bc303dbc2713554f220d64e7bfa262f5973851ccbef217
4
+ data.tar.gz: 9e459c21d4d178b2601c5ccc37b245defce997995e31bf85e82b5f08ceed9758
5
5
  SHA512:
6
- metadata.gz: 1e1a50ea78bc7f9a58e6fb9ea3da2c8601ef26d3b5a4540fe7f4d29f2450c04da387e2510c8adc592e35d43108b887ee172512d8435cdbcde17bd18d72d5fe2a
7
- data.tar.gz: 9c2bafce8c59fc7b0ae56950646de26a3ab58fd167269a14e5f15859f99b46b94753107b4c0f3fc807b53adb83591a161f8cd8c5b38bdb0b6f5daf3063d779b9
6
+ metadata.gz: 6ad4cb08e2ced98d4daaad9bf4f519615b773dacdca507b9e5eaeee0b03cc9f4cb29eb87e62aee815cfb99c99167e71176c63362d3fa8e20b0d1a82dfe8ac112
7
+ data.tar.gz: fd859b7d8ef82f97e4a34b02dcd3b7b077b6cf5f479069d75194dd2bbd9587beb629cad6bf9e7d8573a0ce4cde4c1e80965d868fcb8489f4eddfb8eb3baf09b5
data/.rubocop.yml CHANGED
@@ -1,5 +1,11 @@
1
1
  AllCops:
2
- TargetRubyVersion: 2.6
2
+ TargetRubyVersion: 3.1
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+ Exclude:
6
+ - "coverage/**/*"
7
+ - "vendor/**/*"
8
+ - "*.gemspec"
3
9
 
4
10
  Style/StringLiterals:
5
11
  Enabled: true
@@ -9,16 +15,41 @@ Style/StringLiteralsInInterpolation:
9
15
  Enabled: true
10
16
  EnforcedStyle: double_quotes
11
17
 
18
+ Style/Documentation:
19
+ Enabled: false
20
+
12
21
  Layout/LineLength:
13
22
  Max: 120
14
23
 
15
24
  Metrics/MethodLength:
16
- Max: 20 # Example: Set maximum method length to 15 lines
25
+ Max: 25
17
26
  CountAsOne:
18
27
  - array
19
28
  - hash
20
29
  - heredoc
21
30
  - method_call
31
+ Exclude:
32
+ - "test/**/*"
22
33
 
23
34
  Metrics/AbcSize:
24
- Max: 20
35
+ Max: 25
36
+ Exclude:
37
+ - "test/**/*"
38
+
39
+ Metrics/ParameterLists:
40
+ CountKeywordArgs: false
41
+
42
+ Metrics/ClassLength:
43
+ Max: 200
44
+
45
+ Metrics/ModuleLength:
46
+ Max: 150
47
+
48
+ # PayFast field names use ADDRESS_1, ADDRESS_2 -- mirror them in the
49
+ # option hashes so it's obvious how each key maps to the wire format.
50
+ Naming/VariableNumber:
51
+ Enabled: false
52
+
53
+ Naming/MethodParameterName:
54
+ Exclude:
55
+ - "test/**/*"
data/CHANGELOG.md CHANGED
@@ -1,5 +1,218 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.1.0] - 2026-05-12
4
+
5
+ ### Overview
6
+
7
+ Easypaisa lands — three new REST endpoints + a Rails OTC voucher helper.
8
+ Pure addition; nothing in 1.0 changes shape, no breaking changes.
9
+
10
+ ### Public API (new)
11
+
12
+ ```ruby
13
+ # Mobile Account: customer authorises via OTP push to their Easypaisa wallet
14
+ result = PaygatePk::EasyPaisa::MobileAccount.charge(
15
+ order_id: "lawzo-#{payment.id}",
16
+ amount: 1500,
17
+ mobile_account_no: "03001234567",
18
+ email: "client@example.com"
19
+ )
20
+ result.success? # response_code == "0000"
21
+ result.transaction_id # Ericsson EWP ID
22
+
23
+ # OTC voucher: customer pays cash at any Easypaisa shop
24
+ voucher = PaygatePk::EasyPaisa::OTC.create(
25
+ order_id: "lawzo-#{payment.id}",
26
+ amount: 1500,
27
+ msisdn: "03001234567",
28
+ email: "client@example.com",
29
+ token_expiry: 7.days.from_now
30
+ )
31
+ voucher.payment_token # "40933012" -- show this to the customer
32
+ voucher.expires_at # Time
33
+
34
+ # Inquiry: poll status until PAID
35
+ status = PaygatePk::EasyPaisa::Inquiry.fetch(
36
+ order_id: "lawzo-...",
37
+ account_num: PaygatePk.config.easy_paisa.account_num
38
+ )
39
+ status.paid?
40
+ status.pending?
41
+ ```
42
+
43
+ Rails OTC voucher helper:
44
+
45
+ ```erb
46
+ <%= paygate_pk_otc_voucher(@voucher) %>
47
+ ```
48
+
49
+ ### Added
50
+
51
+ - `PaygatePk::EasyPaisa::MobileAccount.charge` — `initiate-ma-transaction`.
52
+ - `PaygatePk::EasyPaisa::OTC.create` — `initiate-otc-transaction`. Accepts
53
+ Date/Time/DateTime/String for `token_expiry`, formats to
54
+ `"yyyymmdd HHmmss"` internally, validates future-dated up-front.
55
+ - `PaygatePk::EasyPaisa::Inquiry.fetch` — `inquire-transaction`. Returns
56
+ `InquiryResult` with `paid?` / `failed?` / `pending?` / `expired?` /
57
+ `blocked?` / `reversed?` predicates covering every documented
58
+ `transactionStatus` value.
59
+ - `PaygatePk::EasyPaisa::Endpoints` — sandbox URL baked in;
60
+ `c.easy_paisa.base_url = "..."` override for production (Easypaisa
61
+ hands the host out at go-live).
62
+ - `Contracts::ChargeResult` — universal value object for any REST-style
63
+ "take a payment" call. Carries `success?`, `failed?`, `otc?`,
64
+ `mobile_account?` predicates.
65
+ - `Contracts::InquiryResult` — universal value object for status lookups.
66
+ - `Config::EasyPaisaConfig` — `environment` / `username` / `password` /
67
+ `store_id` / `account_num` / `base_url` override.
68
+ - `Util::Credentials.basic(user, pass)` — `Base64.strict_encode64`
69
+ helper for Easypaisa's `Credentials` HTTP header.
70
+ - `Coercions.to_easypaisa_timestamp` — formats Date/Time to the
71
+ `"yyyymmdd HHmmss"` format `tokenExpiry` demands.
72
+ - `PaygatePk::Rails::ViewHelpers#paygate_pk_otc_voucher(charge_result)`
73
+ — prints a styled voucher (token, amount, mobile, expiry, order id,
74
+ instructions). Renders a failure card with `response_message` when
75
+ the upstream call returned a non-`0000` `responseCode`, never a fake
76
+ token.
77
+
78
+ ### Deferred to 1.2
79
+
80
+ - `EasyPaisa::Callback.verify!` — Easypaisa's integration guide mentions
81
+ IPN configuration in the merchant portal but does not ship the full
82
+ wire spec. Until that's published, host apps should poll
83
+ `Inquiry.fetch` after issuing an OTC voucher.
84
+
85
+ ### Not changed
86
+
87
+ - Nothing in the PayFast surface; 1.0's contracts and call sites are
88
+ unchanged.
89
+
90
+ ## [1.0.0] - 2026-05-12
91
+
92
+ ### Overview
93
+
94
+ Complete rewrite focused on **PayFast hosted-checkout (redirection)**.
95
+ The gem now ships everything needed to take a one-time payment in three
96
+ files — initializer, controller, and a one-line ERB. The 0.x server-to-
97
+ server `Checkout` class that POSTed to `PostTransaction` has been removed;
98
+ that endpoint is browser-side per PayFast's Merchant Integration Guide,
99
+ and the 0.x implementation was architecturally wrong.
100
+
101
+ ### Public API (new)
102
+
103
+ ```ruby
104
+ PaygatePk::PayFast::Redirect.build(...) # → Contracts::RedirectRequest
105
+ PaygatePk::PayFast::Callback.verify!(p) # → Contracts::CallbackEvent
106
+ ```
107
+
108
+ Rails view helper:
109
+
110
+ ```erb
111
+ <%= paygate_pk_redirect_form(@redirect, autosubmit: true) %>
112
+ ```
113
+
114
+ ### Added
115
+
116
+ - `PaygatePk::PayFast::Redirect` — fetches an access token, then assembles
117
+ every mandatory + optional PayFast form field documented in Merchant
118
+ Integration Guide v2.3 §3.2. Handles `items[]` arrays, shipping/billing
119
+ blocks, recurring flag, store override, currency override, and
120
+ free-form `extra_fields` passthrough.
121
+ - `PaygatePk::PayFast::Callback` — replaces the 0.x `Webhook` class.
122
+ Down-cases all incoming param keys once at entry so PayFast's
123
+ mixed-case `Recurring_txn`/`Instrument_token`/`PaymentName` and
124
+ lower-snake `basket_id`/`err_code`/`validation_hash` all work without
125
+ caller intervention.
126
+ - `PaygatePk::PayFast::Endpoints` — URL map keyed by environment
127
+ (`:sandbox`, `:production`); `c.pay_fast.base_url=` override still
128
+ supported for custom staging hosts.
129
+ - `PaygatePk::Rails::ViewHelpers#paygate_pk_redirect_form` —
130
+ auto-submitting form helper with CSP-nonce support, autoloaded via
131
+ a Railtie when the gem boots inside a Rails app.
132
+ - `Contracts::RedirectRequest` — `{ provider, action_url, http_method,
133
+ fields, basket_id, amount, token, raw }`.
134
+ - `Contracts::CallbackEvent` — universal IPN/return shape with
135
+ `approved?` predicate. Future Easypaisa callbacks will return the
136
+ same struct.
137
+ - `PaygatePk::Coercions` — `to_iso_date`, `to_amount_string`,
138
+ `blank?`/`present?`.
139
+ - `Config::PayFastConfig#environment` toggle, validated setter.
140
+ - `Config::PayFastConfig#merchant_name` (was hardcoded to `""` in 0.x).
141
+ - `HTTP::Client`: full Faraday error mapping (`TimeoutError`,
142
+ `ConnectionError`, `HTTPError` cover-all), memoised connection,
143
+ log redaction for `SECURED_KEY`/`Authorization`/`Credentials`.
144
+ - `Errors::CapabilityNotSupported`, `Errors::TimeoutError`,
145
+ `Errors::ConnectionError`, `Errors::ProviderError`.
146
+
147
+ ### Changed
148
+
149
+ - Public namespace flattened from `PaygatePk::Providers::PayFast::*`
150
+ to `PaygatePk::PayFast::*`.
151
+ - `Util::Signature::Payfast` renamed to `Util::Signature::PayFast`.
152
+ - `Config#frozen?` renamed to `Config#configured?`. `Config#freeze!`
153
+ now actually deep-freezes (was a no-op `@frozen = true` boolean).
154
+ - `Contracts::AccessToken#token` is now an alias for `#value`; the
155
+ primary field name is `value` to stay consistent with future
156
+ bearer/charge tokens.
157
+ - Required Ruby version: `>= 3.1.0` (matches the 0.x README claim;
158
+ gemspec used to say `>= 2.6.0` despite Faraday 2 effectively needing
159
+ 3.1).
160
+ - `spec.require_paths` reduced from `%w[lib test]` to `["lib"]` so
161
+ test helpers are no longer shipped on the gem load path.
162
+ - `nokogiri` removed from runtime deps (no longer parsing HTML
163
+ responses now that `Checkout` is gone).
164
+ - Test suite rewritten end-to-end. 76 tests / 200 assertions /
165
+ 96 % line coverage / 77 % branch coverage.
166
+
167
+ ### Removed
168
+
169
+ - `Providers::PayFast::Client` — dual facade/base-class
170
+ identity replaced by namespace module + `Endpoints`.
171
+ - `Providers::PayFast::Checkout` — server-to-server POST
172
+ to `PostTransaction` was wrong for the redirection flow.
173
+ - `Providers::PayFast::Webhook` — renamed to `Callback`.
174
+ - `Providers::PayFast::Tokenization::Token`/`Instrument` — deferred
175
+ to 1.2 alongside the saved-instrument charge endpoint. Source
176
+ preserved in git history.
177
+ - `Util::Html` — no longer needed.
178
+ - `Contracts::HostedCheckout` — replaced by
179
+ `RedirectRequest`.
180
+ - `Contracts::WebhookEvent` — renamed to
181
+ `CallbackEvent` with broader field coverage and an `approved?`
182
+ predicate.
183
+ - `Contracts::BearerToken`/`Instrument` — deferred to 1.2.
184
+
185
+ ### Fixed
186
+
187
+ - Callback key-casing bug. The 0.x `Webhook#verify!` only aliased
188
+ two specific PascalCase keys; in production PayFast sends a mix of
189
+ lower-snake and PascalCase keys that the old code couldn't read.
190
+ - `ORDER_DATE` is now coerced to `YYYY-MM-DD` per spec; the 0.x
191
+ flow let timestamped strings through silently.
192
+ - `SIGNATURE` is generated fresh per call (PayFast doc: "A random
193
+ string value"). The 0.x integration in office-management was
194
+ shipping the literal demo string `"SOMERANDOM-STRING"` to
195
+ production.
196
+ - `CURRENCY_CODE` is now plumbed through `Redirect.build`; the 0.x
197
+ `Checkout` read the global `PaygatePk.config.default_currency` and
198
+ ignored per-call values, silently mismatching auth and checkout.
199
+ - All `Faraday::Error` subclasses (timeout, connection failure, 5xx,
200
+ SSL) now surface as typed `PaygatePk` errors instead of escaping
201
+ as raw `Faraday::*` exceptions.
202
+ - `SECURED_KEY` no longer leaks into Rails logs when a debug logger
203
+ is wired up.
204
+ - `Config#freeze!` is now a real deep-freeze.
205
+
206
+ ### Migration from 0.x
207
+
208
+ | 0.x | 1.0 |
209
+ |-----|-----|
210
+ | `PaygatePk::Providers::PayFast::Auth.new.get_access_token(...)` | `PaygatePk::PayFast::Redirect.build(...)` calls `Auth` internally — you usually don't need it directly. |
211
+ | `PaygatePk::Providers::PayFast::Checkout.new.create!(opts: {...})` | `PaygatePk::PayFast::Redirect.build(...)` (kwargs, no `opts:` wrapper). |
212
+ | `PaygatePk::Providers::PayFast::Webhook.new.verify!(params)` | `PaygatePk::PayFast::Callback.verify!(params)` |
213
+ | `c.pay_fast.base_url = "..."` (hand-typed sandbox host) | `c.pay_fast.environment = :sandbox` (override only when PayFast hands you a custom staging URL). |
214
+ | Hand-built 18-field hidden form ERB | `<%= paygate_pk_redirect_form(@redirect) %>` |
215
+
3
216
  ## [0.2.0] - 2025-10-10
4
217
 
5
218
  - Added: IPN verification, Tokenization 3.1 & 3.15.
data/Gemfile.lock CHANGED
@@ -1,24 +1,49 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paygate_pk (0.2.3)
4
+ paygate_pk (1.1.0)
5
5
  faraday (>= 2.7)
6
6
  faraday-retry (>= 2.0)
7
7
  json
8
- nokogiri (>= 1.16, < 2.0)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
12
11
  specs:
12
+ actionview (8.1.3)
13
+ activesupport (= 8.1.3)
14
+ builder (~> 3.1)
15
+ erubi (~> 1.11)
16
+ rails-dom-testing (~> 2.2)
17
+ rails-html-sanitizer (~> 1.6)
18
+ activesupport (8.1.3)
19
+ base64
20
+ bigdecimal
21
+ concurrent-ruby (~> 1.0, >= 1.3.1)
22
+ connection_pool (>= 2.2.5)
23
+ drb
24
+ i18n (>= 1.6, < 2)
25
+ json
26
+ logger (>= 1.4.2)
27
+ minitest (>= 5.1)
28
+ securerandom (>= 0.3)
29
+ tzinfo (~> 2.0, >= 2.0.5)
30
+ uri (>= 0.13.1)
13
31
  addressable (2.8.7)
14
32
  public_suffix (>= 2.0.2, < 7.0)
15
33
  ast (2.4.3)
34
+ base64 (0.3.0)
16
35
  bigdecimal (3.2.3)
36
+ builder (3.3.0)
17
37
  byebug (12.0.0)
38
+ concurrent-ruby (1.3.6)
39
+ connection_pool (3.0.2)
18
40
  crack (1.0.0)
19
41
  bigdecimal
20
42
  rexml
43
+ crass (1.0.6)
21
44
  docile (1.4.1)
45
+ drb (2.2.3)
46
+ erubi (1.13.1)
22
47
  faraday (2.13.1)
23
48
  faraday-net_http (>= 2.0, < 3.5)
24
49
  json
@@ -28,16 +53,21 @@ GEM
28
53
  faraday-retry (2.3.2)
29
54
  faraday (~> 2.0)
30
55
  hashdiff (1.2.1)
56
+ i18n (1.14.8)
57
+ concurrent-ruby (~> 1.0)
31
58
  json (2.12.2)
32
59
  language_server-protocol (3.17.0.5)
33
60
  lint_roller (1.1.0)
34
61
  logger (1.7.0)
62
+ loofah (2.25.1)
63
+ crass (~> 1.0.2)
64
+ nokogiri (>= 1.12.0)
35
65
  minitest (5.25.5)
36
66
  net-http (0.6.0)
37
67
  uri
38
- nokogiri (1.18.9-x86_64-darwin)
68
+ nokogiri (1.19.3-x86_64-darwin)
39
69
  racc (~> 1.4)
40
- nokogiri (1.18.9-x86_64-linux-gnu)
70
+ nokogiri (1.19.3-x86_64-linux-gnu)
41
71
  racc (~> 1.4)
42
72
  parallel (1.27.0)
43
73
  parser (3.3.8.0)
@@ -46,6 +76,13 @@ GEM
46
76
  prism (1.5.1)
47
77
  public_suffix (6.0.2)
48
78
  racc (1.8.1)
79
+ rails-dom-testing (2.3.0)
80
+ activesupport (>= 5.0.0)
81
+ minitest
82
+ nokogiri (>= 1.6)
83
+ rails-html-sanitizer (1.7.0)
84
+ loofah (~> 2.25)
85
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
49
86
  rainbow (3.1.1)
50
87
  rake (13.3.0)
51
88
  regexp_parser (2.10.0)
@@ -65,16 +102,19 @@ GEM
65
102
  parser (>= 3.3.7.2)
66
103
  prism (~> 1.4)
67
104
  ruby-progressbar (1.13.0)
105
+ securerandom (0.4.1)
68
106
  simplecov (0.22.0)
69
107
  docile (~> 1.1)
70
108
  simplecov-html (~> 0.11)
71
109
  simplecov_json_formatter (~> 0.1)
72
110
  simplecov-html (0.13.2)
73
111
  simplecov_json_formatter (0.1.4)
112
+ tzinfo (2.0.6)
113
+ concurrent-ruby (~> 1.0)
74
114
  unicode-display_width (3.1.4)
75
115
  unicode-emoji (~> 4.0, >= 4.0.4)
76
116
  unicode-emoji (4.0.4)
77
- uri (1.0.3)
117
+ uri (1.1.1)
78
118
  webmock (3.25.1)
79
119
  addressable (>= 2.8.0)
80
120
  crack (>= 0.3.2)
@@ -85,6 +125,7 @@ PLATFORMS
85
125
  x86_64-linux
86
126
 
87
127
  DEPENDENCIES
128
+ actionview (>= 7.0)
88
129
  byebug
89
130
  minitest (~> 5.0)
90
131
  paygate_pk!