paygate_pk 0.2.3 → 1.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +31 -3
  3. data/CHANGELOG.md +126 -0
  4. data/Gemfile.lock +46 -5
  5. data/README.md +181 -70
  6. data/lib/paygate_pk/coercions.rb +46 -0
  7. data/lib/paygate_pk/config.rb +68 -24
  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/redirect_request.rb +30 -0
  11. data/lib/paygate_pk/errors.rb +16 -3
  12. data/lib/paygate_pk/http/client.rb +84 -71
  13. data/lib/paygate_pk/pay_fast/auth.rb +79 -0
  14. data/lib/paygate_pk/pay_fast/callback.rb +92 -0
  15. data/lib/paygate_pk/pay_fast/endpoints.rb +38 -0
  16. data/lib/paygate_pk/pay_fast/redirect.rb +227 -0
  17. data/lib/paygate_pk/pay_fast.rb +19 -0
  18. data/lib/paygate_pk/rails/railtie.rb +19 -0
  19. data/lib/paygate_pk/rails/view_helpers.rb +59 -0
  20. data/lib/paygate_pk/util/signature/pay_fast.rb +25 -0
  21. data/lib/paygate_pk/version.rb +1 -1
  22. data/lib/paygate_pk.rb +34 -18
  23. metadata +25 -32
  24. data/lib/paygate_pk/contracts/bearer_token.rb +0 -18
  25. data/lib/paygate_pk/contracts/hosted_checkout.rb +0 -8
  26. data/lib/paygate_pk/contracts/instrument.rb +0 -10
  27. data/lib/paygate_pk/contracts/webhook_event.rb +0 -24
  28. data/lib/paygate_pk/providers/pay_fast/auth.rb +0 -61
  29. data/lib/paygate_pk/providers/pay_fast/checkout.rb +0 -157
  30. data/lib/paygate_pk/providers/pay_fast/client.rb +0 -53
  31. data/lib/paygate_pk/providers/pay_fast/tokenization/instrument.rb +0 -63
  32. data/lib/paygate_pk/providers/pay_fast/tokenization/token.rb +0 -65
  33. data/lib/paygate_pk/providers/pay_fast/webhook.rb +0 -74
  34. data/lib/paygate_pk/util/html.rb +0 -42
  35. data/lib/paygate_pk/util/signature.rb +0 -18
  36. data/paygate_pk.gemspec +0 -46
@@ -0,0 +1,227 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "securerandom"
5
+
6
+ module PaygatePk
7
+ module PayFast
8
+ # Builds the redirect form that the customer's browser submits to
9
+ # PayFast's hosted checkout page.
10
+ #
11
+ # Flow (Merchant Integration Guide v2.3 §3.2):
12
+ # 1. Fetch ACCESS_TOKEN server-to-server (delegated to Auth)
13
+ # 2. Assemble all the PostTransaction form fields (mandatory +
14
+ # optional), with PayFast's UPPER_SNAKE_CASE naming
15
+ # 3. Return a Contracts::RedirectRequest the host app renders
16
+ # as an auto-submitting <form>
17
+ #
18
+ # Usage:
19
+ #
20
+ # redirect = PaygatePk::PayFast::Redirect.build(
21
+ # basket_id: "sp-#{payment.id}",
22
+ # amount: 1500,
23
+ # description: "Subscription",
24
+ # customer: { mobile: "03001234567", email: "buyer@x.com", name: "Talha" },
25
+ # success_url: success_url,
26
+ # failure_url: failure_url,
27
+ # checkout_url: webhooks_pay_fast_url, # optional, IPN backend ping
28
+ # recurring: false
29
+ # )
30
+ class Redirect
31
+ # Address-block field mapping. Preserves the SHIPPING_ADDRESS_CITU
32
+ # typo from the PayFast spec — we mirror what the gateway accepts,
33
+ # not what looks correct.
34
+ SHIPPING_FIELDS = {
35
+ name: "SHIPPING_CUSTOMER_NAME",
36
+ address_1: "SHIPPING_ADDRESS_1",
37
+ address_2: "SHIPPING_ADDRESS_2",
38
+ state: "SHIPPING_STATE_PROVINCE",
39
+ city: "SHIPPING_ADDRESS_CITU",
40
+ postal_code: "SHIPPING_POSTALCODE",
41
+ method: "SHIPPING_METHOD"
42
+ }.freeze
43
+
44
+ BILLING_FIELDS = {
45
+ name: "BILLING_CUSTOMER_NAME",
46
+ city: "BILLING_ADDRESS_CITY",
47
+ address_1: "BILLING_ADDRESS_1",
48
+ address_2: "BILLING_ADDRESS_2",
49
+ state: "BILLING_STATE_PROVINCE",
50
+ postal_code: "BILLING_POSTALCODE"
51
+ }.freeze
52
+
53
+ def self.build(**kwargs)
54
+ new.build(**kwargs)
55
+ end
56
+
57
+ def initialize(config: PaygatePk::PayFast.config, auth: nil)
58
+ @config = config
59
+ @auth = auth
60
+ end
61
+
62
+ def build(basket_id:, amount:, customer:, success_url:, failure_url:, description:,
63
+ currency: nil, order_date: nil, checkout_url: nil, store_id: nil,
64
+ items: [], recurring: false, tran_type: nil, processing_type: nil,
65
+ instrument_token: nil, shipping: nil, billing: nil, country: nil,
66
+ customer_ip: nil, merchant_customer_id: nil, merchant_user_agent: nil,
67
+ transaction_instrument: nil, extra_fields: {})
68
+ currency ||= PaygatePk.config.default_currency
69
+ order_date_str = Coercions.to_iso_date(order_date) || Date.today.strftime("%Y-%m-%d")
70
+
71
+ ensure_config!
72
+ ensure_args!(basket_id: basket_id, amount: amount, customer: customer,
73
+ success_url: success_url, failure_url: failure_url, description: description)
74
+
75
+ token = auth.call(basket_id: basket_id, amount: amount, currency: currency)
76
+
77
+ fields = build_fields(
78
+ token: token.value,
79
+ basket_id: basket_id,
80
+ amount: amount,
81
+ currency: currency,
82
+ customer: customer,
83
+ success_url: success_url,
84
+ failure_url: failure_url,
85
+ checkout_url: checkout_url,
86
+ description: description,
87
+ order_date_str: order_date_str,
88
+ store_id: store_id,
89
+ items: items,
90
+ recurring: recurring,
91
+ tran_type: tran_type,
92
+ processing_type: processing_type,
93
+ instrument_token: instrument_token,
94
+ transaction_instrument: transaction_instrument,
95
+ shipping: shipping,
96
+ billing: billing,
97
+ country: country,
98
+ customer_ip: customer_ip,
99
+ merchant_customer_id: merchant_customer_id,
100
+ merchant_user_agent: merchant_user_agent,
101
+ extra_fields: extra_fields
102
+ )
103
+
104
+ Contracts::RedirectRequest.new(
105
+ provider: :pay_fast,
106
+ action_url: Endpoints.post_transaction_url(@config.resolved_base_url),
107
+ http_method: :post,
108
+ fields: fields,
109
+ basket_id: basket_id.to_s,
110
+ amount: Coercions.to_amount_string(amount),
111
+ token: token.value,
112
+ raw: token.raw
113
+ )
114
+ end
115
+
116
+ private
117
+
118
+ def auth
119
+ @auth ||= Auth.new(config: @config)
120
+ end
121
+
122
+ def ensure_config!
123
+ missing = []
124
+ missing << :merchant_id if Coercions.blank?(@config.merchant_id)
125
+ missing << :secured_key if Coercions.blank?(@config.secured_key)
126
+ missing << :merchant_name if Coercions.blank?(@config.merchant_name)
127
+ return if missing.empty?
128
+
129
+ raise PaygatePk::ConfigurationError, "PayFast config missing: #{missing.join(", ")}"
130
+ end
131
+
132
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
133
+ def ensure_args!(basket_id:, amount:, customer:, success_url:, failure_url:, description:)
134
+ missing = []
135
+ missing << :basket_id if Coercions.blank?(basket_id)
136
+ missing << :amount if amount.nil?
137
+ missing << :success_url if Coercions.blank?(success_url)
138
+ missing << :failure_url if Coercions.blank?(failure_url)
139
+ missing << :description if Coercions.blank?(description)
140
+ missing << "customer.mobile" if !customer.is_a?(Hash) || Coercions.blank?(customer[:mobile])
141
+ missing << "customer.email" if !customer.is_a?(Hash) || Coercions.blank?(customer[:email])
142
+ return if missing.empty?
143
+
144
+ raise PaygatePk::ValidationError.new(
145
+ "missing required args: #{missing.join(", ")}",
146
+ details: { missing: missing }
147
+ )
148
+ end
149
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
150
+
151
+ def build_fields(**opts)
152
+ fields = {}
153
+ add_mandatory_fields!(fields, opts)
154
+ add_optional_simple_fields!(fields, opts)
155
+ add_address_fields!(fields, "SHIPPING", opts[:shipping]) if opts[:shipping]
156
+ add_address_fields!(fields, "BILLING", opts[:billing]) if opts[:billing]
157
+ add_items_fields!(fields, opts[:items]) if opts[:items].is_a?(Array) && !opts[:items].empty?
158
+ add_extra_fields!(fields, opts[:extra_fields])
159
+ fields
160
+ end
161
+
162
+ # rubocop:disable Metrics/AbcSize
163
+ def add_mandatory_fields!(fields, opts)
164
+ fields["MERCHANT_ID"] = @config.merchant_id
165
+ fields["MERCHANT_NAME"] = @config.merchant_name
166
+ fields["TOKEN"] = opts[:token]
167
+ fields["PROCCODE"] = "00"
168
+ fields["TXNAMT"] = Coercions.to_amount_string(opts[:amount])
169
+ fields["CUSTOMER_MOBILE_NO"] = opts[:customer][:mobile].to_s
170
+ fields["CUSTOMER_EMAIL_ADDRESS"] = opts[:customer][:email].to_s
171
+ # SIGNATURE and VERSION are documented as "A random string value"
172
+ # in Merchant Integration Guide v2.3 §3.2 — they are not crypto
173
+ # signatures. We generate a fresh random per request.
174
+ fields["SIGNATURE"] = SecureRandom.hex(16)
175
+ fields["VERSION"] = @config.version_string || "paygate_pk/#{PaygatePk::VERSION}"
176
+ fields["TXNDESC"] = opts[:description]
177
+ fields["SUCCESS_URL"] = opts[:success_url]
178
+ fields["FAILURE_URL"] = opts[:failure_url]
179
+ fields["BASKET_ID"] = opts[:basket_id].to_s
180
+ fields["ORDER_DATE"] = opts[:order_date_str]
181
+ fields["CURRENCY_CODE"] = opts[:currency]
182
+ fields["TRAN_TYPE"] = opts[:tran_type] || @config.tran_type
183
+ end
184
+ # rubocop:enable Metrics/AbcSize
185
+
186
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
187
+ def add_optional_simple_fields!(fields, opts)
188
+ fields["CHECKOUT_URL"] = opts[:checkout_url] if opts[:checkout_url]
189
+ store_id = opts[:store_id] || @config.store_id
190
+ fields["STORE_ID"] = store_id if Coercions.present?(store_id)
191
+ fields["RECURRING_TXN"] = opts[:recurring] ? "TRUE" : "FALSE"
192
+ fields["CUSTOMER_NAME"] = opts[:customer][:name].to_s if opts[:customer][:name]
193
+ fields["CUSTOMER_IPADDRESS"] = opts[:customer_ip] if opts[:customer_ip]
194
+ fields["MERCHANT_CUSTOMER_ID"] = opts[:merchant_customer_id] if opts[:merchant_customer_id]
195
+ fields["MERCHANT_USERAGENT"] = opts[:merchant_user_agent] if opts[:merchant_user_agent]
196
+ fields["COUNTRY"] = opts[:country] if opts[:country]
197
+ fields["PROCESSING_TYPE"] = opts[:processing_type] if opts[:processing_type]
198
+ fields["INSTRUMENT_TOKEN"] = opts[:instrument_token] if opts[:instrument_token]
199
+ fields["Transaction_Instrument"] = opts[:transaction_instrument].to_s if opts[:transaction_instrument]
200
+ end
201
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
202
+
203
+ def add_address_fields!(fields, prefix, hash)
204
+ map = prefix == "SHIPPING" ? SHIPPING_FIELDS : BILLING_FIELDS
205
+ map.each do |key, payfast_name|
206
+ v = hash[key]
207
+ fields[payfast_name] = v.to_s if Coercions.present?(v)
208
+ end
209
+ end
210
+
211
+ def add_items_fields!(fields, items)
212
+ items.each_with_index do |item, i|
213
+ fields["ITEMS[#{i}][SKU]"] = item[:sku].to_s if item[:sku]
214
+ fields["ITEMS[#{i}][NAME]"] = item[:name].to_s if item[:name]
215
+ fields["ITEMS[#{i}][PRICE]"] = Coercions.to_amount_string(item[:price]) if item[:price]
216
+ fields["ITEMS[#{i}][QTY]"] = item[:qty].to_s if item[:qty]
217
+ end
218
+ end
219
+
220
+ def add_extra_fields!(fields, extras)
221
+ return unless extras.is_a?(Hash)
222
+
223
+ extras.each { |k, v| fields[k.to_s] = v.to_s }
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaygatePk
4
+ # PayFast integration.
5
+ #
6
+ # Public API (1.0):
7
+ #
8
+ # PaygatePk::PayFast::Redirect.build(...) # => Contracts::RedirectRequest
9
+ # PaygatePk::PayFast::Callback.verify!(p) # => Contracts::CallbackEvent
10
+ #
11
+ # Internal helpers (Auth, Endpoints) are loaded but not part of the
12
+ # advertised surface — they may change without a major version bump.
13
+ module PayFast
14
+ # Shortcut to the provider-scoped config block.
15
+ def self.config
16
+ PaygatePk.config.pay_fast
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/railtie"
4
+ require_relative "view_helpers"
5
+
6
+ module PaygatePk
7
+ module Rails
8
+ # Wires ViewHelpers into ActionView when the gem boots inside a
9
+ # Rails app. Loaded conditionally from lib/paygate_pk.rb so that
10
+ # non-Rails consumers don't have to install Rails to use the gem.
11
+ class Railtie < ::Rails::Railtie
12
+ initializer "paygate_pk.view_helpers" do
13
+ ActiveSupport.on_load(:action_view) do
14
+ include PaygatePk::Rails::ViewHelpers
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PaygatePk
4
+ module Rails
5
+ # Action View helpers, auto-included into ActionView::Base by the
6
+ # Railtie when the gem boots inside a Rails app.
7
+ module ViewHelpers
8
+ DEFAULT_FORM_ID = "paygate-pk-redirect-form"
9
+ SUBMIT_LABEL = "Pay now"
10
+
11
+ # Renders the auto-submitting redirect form for any provider that
12
+ # produces a Contracts::RedirectRequest.
13
+ #
14
+ # <%= paygate_pk_redirect_form(@redirect) %>
15
+ #
16
+ # Options:
17
+ # autosubmit: true (default) — emits a script tag that submits
18
+ # the form on page load. Set to false to render a
19
+ # visible "Pay now" button instead.
20
+ # html: { id:, class:, data: { ... }, ... } — passed
21
+ # straight to the <form> tag. `id` falls back to
22
+ # "paygate-pk-redirect-form".
23
+ # submit_label: button label when autosubmit is false.
24
+ def paygate_pk_redirect_form(redirect, autosubmit: true, html: {}, submit_label: SUBMIT_LABEL)
25
+ form_id = html[:id] || DEFAULT_FORM_ID
26
+ form_html = render_form(redirect, form_id, html, autosubmit, submit_label)
27
+ return form_html unless autosubmit
28
+
29
+ form_html + autosubmit_script(form_id)
30
+ end
31
+
32
+ private
33
+
34
+ def render_form(redirect, form_id, html, autosubmit, submit_label)
35
+ attrs = {
36
+ id: form_id,
37
+ action: redirect.action_url,
38
+ method: redirect.http_method.to_s,
39
+ accept_charset: "UTF-8"
40
+ }.merge(html.except(:id))
41
+
42
+ content_tag(:form, attrs) do
43
+ hidden = redirect.fields.map do |name, value|
44
+ tag.input(type: "hidden", name: name.to_s, value: value.to_s)
45
+ end
46
+ submit = autosubmit ? "".html_safe : tag.button(submit_label, type: "submit")
47
+ safe_join(hidden + [submit])
48
+ end
49
+ end
50
+
51
+ def autosubmit_script(form_id)
52
+ javascript_tag(
53
+ "document.getElementById(#{form_id.to_json}).submit();",
54
+ nonce: respond_to?(:content_security_policy_nonce) ? content_security_policy_nonce : nil
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "openssl"
4
+
5
+ module PaygatePk
6
+ module Util
7
+ module Signature
8
+ # PayFast IPN/return validation_hash computation.
9
+ #
10
+ # Per Merchant Integration Guide v2.3 §3.2.3:
11
+ # validation_hash = SHA256("basket_id|secured_key|merchant_id|err_code")
12
+ #
13
+ # Returned as a hex digest. Compared in constant time on the receiving
14
+ # side via PaygatePk::Util::Security.secure_compare.
15
+ module PayFast
16
+ SEPARATOR = "|"
17
+
18
+ def self.validation_hash(basket_id:, merchant_secret_key:, merchant_id:, payfast_err_code:)
19
+ data = [basket_id, merchant_secret_key, merchant_id, payfast_err_code].join(SEPARATOR)
20
+ OpenSSL::Digest::SHA256.hexdigest(data)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PaygatePk
4
- VERSION = "0.2.3"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/paygate_pk.rb CHANGED
@@ -2,37 +2,53 @@
2
2
 
3
3
  require_relative "paygate_pk/version"
4
4
  require_relative "paygate_pk/errors"
5
+ require_relative "paygate_pk/coercions"
5
6
  require_relative "paygate_pk/config"
7
+
8
+ require_relative "paygate_pk/util/security"
9
+ require_relative "paygate_pk/util/signature/pay_fast"
10
+
6
11
  require_relative "paygate_pk/http/client"
7
12
 
8
- # Contracts used by the endpoint
9
13
  require_relative "paygate_pk/contracts/access_token"
10
- require_relative "paygate_pk/contracts/hosted_checkout"
11
- require_relative "paygate_pk/contracts/bearer_token"
12
- require_relative "paygate_pk/contracts/instrument"
13
- require_relative "paygate_pk/contracts/webhook_event"
14
-
15
- # PayFast
16
- require_relative "paygate_pk/providers/pay_fast/client"
17
- require_relative "paygate_pk/providers/pay_fast/auth"
18
- require_relative "paygate_pk/providers/pay_fast/checkout"
19
- require_relative "paygate_pk/providers/pay_fast/webhook"
20
- require_relative "paygate_pk/providers/pay_fast/tokenization/token"
21
-
22
- require_relative "paygate_pk/util/html"
23
- require_relative "paygate_pk/util/signature"
24
- require_relative "paygate_pk/util/security"
14
+ require_relative "paygate_pk/contracts/redirect_request"
15
+ require_relative "paygate_pk/contracts/callback_event"
25
16
 
26
- # Main module for PaygatePk
17
+ require_relative "paygate_pk/pay_fast"
18
+ require_relative "paygate_pk/pay_fast/endpoints"
19
+ require_relative "paygate_pk/pay_fast/auth"
20
+ require_relative "paygate_pk/pay_fast/redirect"
21
+ require_relative "paygate_pk/pay_fast/callback"
22
+
23
+ require_relative "paygate_pk/rails/railtie" if defined?(Rails::Railtie)
24
+
25
+ # Unified Ruby/Rails client for Pakistani payment gateways.
26
+ #
27
+ # PaygatePk.configure do |c|
28
+ # c.pay_fast.environment = :sandbox
29
+ # c.pay_fast.merchant_id = ENV["PAYFAST_MERCHANT_ID"]
30
+ # c.pay_fast.secured_key = ENV["PAYFAST_SECURED_KEY"]
31
+ # c.pay_fast.merchant_name = "Acme Store"
32
+ # end
33
+ #
34
+ # redirect = PaygatePk::PayFast::Redirect.build(...)
35
+ # event = PaygatePk::PayFast::Callback.verify!(request.parameters)
27
36
  module PaygatePk
28
37
  class << self
29
38
  def configure
30
39
  yield(config)
31
40
  config.freeze!
41
+ config
32
42
  end
33
43
 
34
44
  def config
35
- @config ||= PaygatePk::Config.new
45
+ @config ||= Config.new
46
+ end
47
+
48
+ # Test/dev helper. Discards the current (possibly frozen) config so
49
+ # that a fresh PaygatePk.configure call can re-seed it.
50
+ def reset_config!
51
+ @config = Config.new
36
52
  end
37
53
  end
38
54
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paygate_pk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Talha Junaid
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-10 00:00:00.000000000 Z
11
+ date: 2026-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -53,25 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: nokogiri
56
+ name: actionview
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '1.16'
62
- - - "<"
63
- - !ruby/object:Gem::Version
64
- version: '2.0'
65
- type: :runtime
61
+ version: '7.0'
62
+ type: :development
66
63
  prerelease: false
67
64
  version_requirements: !ruby/object:Gem::Requirement
68
65
  requirements:
69
66
  - - ">="
70
67
  - !ruby/object:Gem::Version
71
- version: '1.16'
72
- - - "<"
73
- - !ruby/object:Gem::Version
74
- version: '2.0'
68
+ version: '7.0'
75
69
  - !ruby/object:Gem::Dependency
76
70
  name: byebug
77
71
  requirement: !ruby/object:Gem::Requirement
@@ -156,9 +150,10 @@ dependencies:
156
150
  - - ">="
157
151
  - !ruby/object:Gem::Version
158
152
  version: '0'
159
- description: |-
160
- Provider-agnostic Ruby/Rails client for PayFast: checkout, webhooks/IPN verification,
161
- tokenized & recurring payments.
153
+ description: PaygatePk is a provider-agnostic Ruby/Rails client for payment gateways
154
+ operating in Pakistan. 1.0 ships PayFast hosted-checkout (redirection) and callback
155
+ verification with a one-line Rails view helper. Easypaisa REST (MA/OTC/Inquiry)
156
+ lands in 1.1.
162
157
  email:
163
158
  - talhajunaid65@gmail.com
164
159
  executables: []
@@ -174,43 +169,41 @@ files:
174
169
  - README.md
175
170
  - Rakefile
176
171
  - lib/paygate_pk.rb
172
+ - lib/paygate_pk/coercions.rb
177
173
  - lib/paygate_pk/config.rb
178
174
  - lib/paygate_pk/contracts/access_token.rb
179
- - lib/paygate_pk/contracts/bearer_token.rb
180
- - lib/paygate_pk/contracts/hosted_checkout.rb
181
- - lib/paygate_pk/contracts/instrument.rb
182
- - lib/paygate_pk/contracts/webhook_event.rb
175
+ - lib/paygate_pk/contracts/callback_event.rb
176
+ - lib/paygate_pk/contracts/redirect_request.rb
183
177
  - lib/paygate_pk/errors.rb
184
178
  - lib/paygate_pk/http/client.rb
185
- - lib/paygate_pk/providers/pay_fast/auth.rb
186
- - lib/paygate_pk/providers/pay_fast/checkout.rb
187
- - lib/paygate_pk/providers/pay_fast/client.rb
188
- - lib/paygate_pk/providers/pay_fast/tokenization/instrument.rb
189
- - lib/paygate_pk/providers/pay_fast/tokenization/token.rb
190
- - lib/paygate_pk/providers/pay_fast/webhook.rb
191
- - lib/paygate_pk/util/html.rb
179
+ - lib/paygate_pk/pay_fast.rb
180
+ - lib/paygate_pk/pay_fast/auth.rb
181
+ - lib/paygate_pk/pay_fast/callback.rb
182
+ - lib/paygate_pk/pay_fast/endpoints.rb
183
+ - lib/paygate_pk/pay_fast/redirect.rb
184
+ - lib/paygate_pk/rails/railtie.rb
185
+ - lib/paygate_pk/rails/view_helpers.rb
192
186
  - lib/paygate_pk/util/security.rb
193
- - lib/paygate_pk/util/signature.rb
187
+ - lib/paygate_pk/util/signature/pay_fast.rb
194
188
  - lib/paygate_pk/version.rb
195
- - paygate_pk.gemspec
196
189
  - sig/paygate_pk.rbs
197
190
  homepage: https://github.com/qbitechs/paygate_pk
198
191
  licenses:
199
192
  - MIT
200
193
  metadata:
201
194
  source_code_uri: https://github.com/qbitechs/paygate_pk
202
- changelog_uri: https://github.com/qbitechs/paygate_pk/blob/main/CHANGELOG.md
195
+ changelog_uri: https://github.com/qbitechs/paygate_pk/blob/master/CHANGELOG.md
203
196
  homepage_url: https://github.com/qbitechs/paygate_pk
197
+ rubygems_mfa_required: 'true'
204
198
  post_install_message:
205
199
  rdoc_options: []
206
200
  require_paths:
207
201
  - lib
208
- - test
209
202
  required_ruby_version: !ruby/object:Gem::Requirement
210
203
  requirements:
211
204
  - - ">="
212
205
  - !ruby/object:Gem::Version
213
- version: 2.6.0
206
+ version: 3.1.0
214
207
  required_rubygems_version: !ruby/object:Gem::Requirement
215
208
  requirements:
216
209
  - - ">="
@@ -220,5 +213,5 @@ requirements: []
220
213
  rubygems_version: 3.4.3
221
214
  signing_key:
222
215
  specification_version: 4
223
- summary: Unified Ruby wrapper for PayFast
216
+ summary: Unified Ruby/Rails client for Pakistani payment gateways (PayFast, Easypaisa)
224
217
  test_files: []
@@ -1,18 +0,0 @@
1
- # lib/paygate_pk/contracts/access_token.rb
2
- # frozen_string_literal: true
3
-
4
- module PaygatePk
5
- module Contracts
6
- # Normalized wrapper for /token response.
7
- # Some docs show only "refresh_token"; we expose both.
8
- BearerToken = Struct.new(
9
- :access_token, # String or nil
10
- :refresh_token, # String or nil
11
- :expiry, # Integer seconds or nil
12
- :code, # Provider code string or nil
13
- :message, # Provider message string or nil
14
- :raw, # Full response Hash
15
- keyword_init: true
16
- )
17
- end
18
- end
@@ -1,8 +0,0 @@
1
- # lib/paygate_pk/contracts/access_token.rb
2
- # frozen_string_literal: true
3
-
4
- module PaygatePk
5
- module Contracts
6
- HostedCheckout = Struct.new(:provider, :basket_id, :amount, :url, :raw, :form, keyword_init: true)
7
- end
8
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PaygatePk
4
- module Contracts
5
- Instrument = Struct.new(
6
- :instrument_token, :account_type, :description, :instrument_alias, :raw,
7
- keyword_init: true
8
- )
9
- end
10
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PaygatePk
4
- module Contracts
5
- # Normalized server-side notification from PayFast (IPN) or other providers.
6
- WebhookEvent = Struct.new(
7
- :provider, # Symbol e.g., :payfast
8
- :transaction_id, # String or nil
9
- :basket_id, # String
10
- :order_date, # String (YYYY-MM-DD) or Time/Date if you coerce later
11
- :approved, # Boolean (true if err_code == "000")
12
- :code, # Provider code, e.g., "000"
13
- :message, # Human-readable message
14
- :amount, # String/Integer (as received)
15
- :currency, # String "PKR" etc.
16
- :instrument_token, # String or nil (for tokenized flows)
17
- :recurring, # Boolean
18
- :payment_method,
19
- :merchant_amount,
20
- :raw, # Original params Hash (unmodified input)
21
- keyword_init: true
22
- )
23
- end
24
- end