active_merchant_ecpay 0.1.2 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f6f1486db7a5d1d4e59e71ac6a8728da6770912a
4
- data.tar.gz: feef310fb566b03580f2ffd5dfe00805f11c9f35
2
+ SHA256:
3
+ metadata.gz: 56b85be12292253c0939edcc92e46e146fc283c499a2472ce117edfa57e0f0f0
4
+ data.tar.gz: 5086c6c4bcbaa25a8b62fe47b72cf651d27589f1ad55675d2c601f87c788659f
5
5
  SHA512:
6
- metadata.gz: 593db06557415056377b8a4590ba088b28a8729660d443e4eedb0f8a45d7c43c8cf5965eb8758668e62c838b1418c76b4be9039c35c9dacb7c8b3c99bc38ff87
7
- data.tar.gz: edcf3f6cab3e0847db0b49d1206d6e257d7e7865603e66c8e80c9216c8abf8d1775c372359b916576b771595ecb29b52ba800f2df1de7c2579206d25028ffaf3
6
+ metadata.gz: 58a03448ac707c0e4cd1d47d8d153b1a7446f8ee2303a8338357f4d6e5c66d0e7d3c2e619e3651278f69f5533aa79ed35ef29056955cae4aed02090cb008a815
7
+ data.tar.gz: 125668f6a3bfbef5d0671d80da43a7f2a61f02e5b76f0f07ca6f25212da003923bf57035231666c63674e2a7d6ccbaaad2cbacb477f76db1663b935fc298a780
@@ -0,0 +1,121 @@
1
+ name: Publish gem
2
+
3
+ # master 上 gemspec 版本號尚未發佈到 rubygems.org 時,自動以 Trusted Publishing (OIDC) 發佈。
4
+ # 版本已存在則跳過;不使用任何儲存的 RubyGems API key。
5
+ on:
6
+ push:
7
+ branches:
8
+ - master
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ concurrency:
15
+ group: publish-gem-${{ github.repository }}
16
+ cancel-in-progress: false
17
+
18
+ env:
19
+ # 與 test.yml 矩陣中的 Ruby 3.4 / Rails 8.0 組合一致(gemfiles/ 的 lockfile 不納入版控,由 CI 解析)
20
+ RUBY_VERSION: '3.4.11'
21
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_8.0.gemfile
22
+ GEM_NAME: active_merchant_ecpay
23
+ GEMSPEC: active_merchant_ecpay.gemspec
24
+
25
+ jobs:
26
+ check:
27
+ name: Check version on rubygems.org
28
+ runs-on: ubuntu-latest
29
+ outputs:
30
+ should_publish: ${{ steps.check.outputs.should_publish }}
31
+ gem_name: ${{ steps.spec.outputs.name }}
32
+ gem_version: ${{ steps.spec.outputs.version }}
33
+ steps:
34
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
+ with:
36
+ persist-credentials: false
37
+
38
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
39
+ with:
40
+ ruby-version: ${{ env.RUBY_VERSION }}
41
+
42
+ - name: Read gem name and version from gemspec
43
+ id: spec
44
+ run: |
45
+ ruby -e 'spec = Gem::Specification.load(ENV.fetch("GEMSPEC")) or abort("cannot load #{ENV["GEMSPEC"]}"); abort("gemspec name #{spec.name} != #{ENV["GEM_NAME"]}") unless spec.name == ENV["GEM_NAME"]; puts "name=#{spec.name}"; puts "version=#{spec.version}"' >> "$GITHUB_OUTPUT"
46
+ cat "$GITHUB_OUTPUT"
47
+
48
+ - name: Check whether this version already exists on rubygems.org
49
+ id: check
50
+ env:
51
+ GEM_NAME: ${{ steps.spec.outputs.name }}
52
+ GEM_VERSION: ${{ steps.spec.outputs.version }}
53
+ run: |
54
+ set -euo pipefail
55
+ status=$(curl -sS -o versions.json -w '%{http_code}' "https://rubygems.org/api/v1/versions/${GEM_NAME}.json")
56
+ if [ "$status" = "404" ]; then
57
+ echo "Gem ${GEM_NAME} not found on rubygems.org; will publish."
58
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
59
+ exit 0
60
+ fi
61
+ if [ "$status" != "200" ]; then
62
+ echo "::error::rubygems.org API returned HTTP ${status}"
63
+ exit 1
64
+ fi
65
+ if jq -e --arg v "$GEM_VERSION" 'any(.[]; .number == $v)' versions.json > /dev/null; then
66
+ echo "${GEM_NAME} ${GEM_VERSION} already on rubygems.org; skip publish."
67
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
68
+ else
69
+ echo "${GEM_NAME} ${GEM_VERSION} not on rubygems.org yet; will publish."
70
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
71
+ fi
72
+
73
+ test:
74
+ name: Test (Ruby 3.4 / Rails 8.0)
75
+ needs: check
76
+ if: needs.check.outputs.should_publish == 'true'
77
+ runs-on: ubuntu-latest
78
+ steps:
79
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
80
+ with:
81
+ persist-credentials: false
82
+
83
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
84
+ with:
85
+ ruby-version: ${{ env.RUBY_VERSION }}
86
+ bundler-cache: true
87
+
88
+ - run: bundle exec rake t
89
+
90
+ publish:
91
+ name: Publish ${{ needs.check.outputs.gem_name }} ${{ needs.check.outputs.gem_version }}
92
+ needs: [check, test]
93
+ if: needs.check.outputs.should_publish == 'true' && github.ref == 'refs/heads/master'
94
+ runs-on: ubuntu-latest
95
+ environment: gem-publish
96
+ permissions:
97
+ id-token: write # Trusted Publishing (OIDC)
98
+ contents: write # rake release 推送 vX.Y.Z tag
99
+ steps:
100
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
101
+ with:
102
+ persist-credentials: false
103
+
104
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
105
+ with:
106
+ ruby-version: ${{ env.RUBY_VERSION }}
107
+ bundler-cache: true
108
+
109
+ # setup-ruby 的 bundler-cache 會把 BUNDLE_PATH 等設定寫進 .bundle/config,
110
+ # 若該檔被 git 追蹤,rake release 的 guard_clean 會判定工作目錄不乾淨。
111
+ # 目前此 repo 沒有追蹤任何 .bundle/config;這一步對「有追蹤才標記」做容錯處理,
112
+ # 日後若加入 gemfiles/.bundle/config 或 .bundle/config 也不會讓發佈失敗。
113
+ - name: Ignore CI-only bundler config change
114
+ run: |
115
+ git status --porcelain
116
+ git ls-files -z '*.bundle/config' | xargs -0 -r git update-index --assume-unchanged
117
+ git status --porcelain
118
+
119
+ # 透過 rubygems/configure-rubygems-credentials 取得短效 OIDC token,
120
+ # 再執行 bundle exec rake release(build、建立並推送 vX.Y.Z tag、push gem)
121
+ - uses: rubygems/release-gem@7f9650160c1a4e7989fdc9855807bdbd421d8b6b # v1.4.1
@@ -0,0 +1,47 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ test:
14
+ name: Ruby ${{ matrix.ruby }} / Rails ${{ matrix.rails }}
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ ruby: ["3.0", "3.1", "3.2", "3.3", "3.4"]
20
+ rails: ["7.0", "7.1", "7.2", "8.0", "8.1"]
21
+ exclude:
22
+ # Rails 7.2 需要 Ruby >= 3.1
23
+ - ruby: "3.0"
24
+ rails: "7.2"
25
+ # Rails 8.0 / 8.1 需要 Ruby >= 3.2
26
+ - ruby: "3.0"
27
+ rails: "8.0"
28
+ - ruby: "3.0"
29
+ rails: "8.1"
30
+ - ruby: "3.1"
31
+ rails: "8.0"
32
+ - ruby: "3.1"
33
+ rails: "8.1"
34
+ env:
35
+ BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/rails_${{ matrix.rails }}.gemfile
36
+ steps:
37
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
38
+ with:
39
+ persist-credentials: false
40
+
41
+ - uses: ruby/setup-ruby@b1da4809b005e0178fe08a1d57317bffc00b7b4c # v1.332.0
42
+ with:
43
+ ruby-version: ${{ matrix.ruby }}
44
+ bundler-cache: true
45
+
46
+ - name: Run tests
47
+ run: bundle exec rake t
data/.gitignore CHANGED
@@ -10,3 +10,5 @@
10
10
 
11
11
  # rspec failure tracking
12
12
  .rspec_status
13
+ /gemfiles/*.gemfile.lock
14
+ /gemfiles/.bundle/
data/CHANGELOG.md ADDED
@@ -0,0 +1,42 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Release dates are the dates the version was published on [rubygems.org](https://rubygems.org/gems/active_merchant_ecpay).
9
+
10
+ ## [Unreleased]
11
+
12
+ > `lib/active_merchant_ecpay/version.rb` on `master` is already `0.2.0`, but that version is **not on rubygems.org yet**
13
+ > (the "Publish gem" run for it failed with "No trusted publisher configured for this workflow"), so it is listed here.
14
+ > Once 0.2.0 is published, rename this section to `## [0.2.0] - YYYY-MM-DD` and use the date shown on rubygems.org.
15
+
16
+ ### Added
17
+
18
+ - Support for Rails 7.0, 7.1, 7.2, 8.0 and 8.1 on Ruby 3.0 – 3.4. `actionview` and `activesupport` (`>= 7.0`, `< 8.2`) are now runtime dependencies.
19
+ - RSpec specs for request building, `CheckMacValue`, notification checks and configuration; they never touch the network. SimpleCov coverage with `COVERAGE=1`.
20
+ - GitHub Actions: a Ruby × Rails test matrix (`test.yml`) and automatic publishing to RubyGems through Trusted Publishing (`publish.yml`).
21
+ - `CHANGELOG.md`, a full `README.md`, and documentation rules in `CLAUDE.md`.
22
+
23
+ ### Changed
24
+
25
+ - **Breaking:** requires Ruby `>= 3.0` (`required_ruby_version`); Rails older than 7.0 is no longer supported.
26
+ - Development dependencies were modernised (`bundler >= 2`, `rake >= 13`); `mocha` and the old `rails >= 2.3.14` development dependency were removed in favour of per-version `gemfiles/`.
27
+
28
+ ## [0.1.2] - 2018-11-16
29
+
30
+ Versions `0.1.0` and `0.1.1` were never published to RubyGems, so their changes ship in this release.
31
+
32
+ ### Added
33
+
34
+ - ECPay (綠界科技) AIO checkout integration for ActiveMerchant / OffsitePayments: the `payment_service_for` form helper with `CheckMacValue` (MD5) generation, and `Notification` parsing / verification. Payment methods: credit card, ATM, CVS, barcode and Alipay constants. (0.1.0)
35
+ - `Notification#card4no`: last four digits of the credit card. (0.1.1)
36
+
37
+ ### Changed
38
+
39
+ - Gemspec description and `require` clean-up (`money` is now required explicitly).
40
+
41
+ [Unreleased]: https://github.com/superlanding/active_merchant_ecpay/commits/master
42
+ [0.1.2]: https://rubygems.org/gems/active_merchant_ecpay/versions/0.1.2
data/CLAUDE.md ADDED
@@ -0,0 +1,20 @@
1
+ # CLAUDE.md
2
+
3
+ ## 發版規則(Ruby gem)
4
+
5
+ - 這個 gem(`active_merchant_ecpay`,gemspec:`active_merchant_ecpay.gemspec`)在 master 收到「新版本號」時,由 GitHub Actions(`.github/workflows/publish.yml`)自動發佈到 rubygems.org(Trusted Publishing,不使用 API key)。
6
+ - **每一個 PR 都必須跳版號**(改 `lib/active_merchant_ecpay/version.rb`),除非這個 PR 只改文件、CI 或 CLAUDE.md 且明確不需要發版。沒跳版號的 PR 合進 master 後不會發佈。
7
+ - 跳版規則(Semantic Versioning):修 bug 跳 patch(x.y.Z);新增相容功能跳 minor(x.Y.0);不相容變更跳 major(X.0.0)。
8
+ - 版號只改在 PR 裡。AI 帳號只能開 branch 與 PR,不得合併到 master;只有真人帳號可以合併。合併就等於批准發佈。
9
+ - 不得為了讓測試通過而改變程式原本的行為。
10
+
11
+ ## 文件與更新紀錄(README / CHANGELOG)
12
+
13
+ - **每一個 PR 都必須同步更新文件**:
14
+ - 只要行為、用法、設定、相依套件或支援的 Ruby / Rails 版本有變,就要更新 `README.md`。
15
+ - 一律要在 `CHANGELOG.md` 新增一筆條目([Keep a Changelog](https://keepachangelog.com/zh-TW/1.1.0/) 格式,分類用 Added / Changed / Deprecated / Removed / Fixed / Security)。
16
+ - `CHANGELOG.md` 的寫法:
17
+ - 沒有跳版號的 PR:寫在最上方的 `## [Unreleased]` 底下。
18
+ - 有跳版號的 PR:把條目寫在新的版本章節 `## [x.y.z] - YYYY-MM-DD`(日期為合併日),並同步更新檔尾的版本比較連結。
19
+ - 版本號必須與 rubygems.org 上實際發佈的版本一致,不得寫還沒發佈的版本。
20
+ - 只改文件、CI 或 CLAUDE.md 的 PR 不需要跳版號,但仍要在 `CHANGELOG.md` 的 `Unreleased` 補一筆。
data/README.md CHANGED
@@ -1,35 +1,67 @@
1
1
  # ActiveMerchantEcpay
2
2
 
3
- This plugin is an active_merchant patch for ecpay(歐付寶) online payment in Taiwan.
4
- Now it supports:
5
- - Credit card(信用卡)
6
- - ATM(虛擬ATM)
7
- - BARCODE(超商條碼).
3
+ [ActiveMerchant](https://github.com/activemerchant/active_merchant) / [OffsitePayments](https://github.com/activemerchant/offsite_payments) integration for **ECPay (綠界科技 / 歐付寶)** AIO checkout in Taiwan, for Rails. It lets a Rails app:
8
4
 
9
- It has been tested on Rails 4.2 successfully.
5
+ - render the hidden-field form that sends a customer to the ECPay payment page (including the `CheckMacValue` signature), and
6
+ - parse and verify the server notification (`ReturnURL`) ECPay posts back.
7
+
8
+ Payment methods supported by the helper constants: credit card (`Credit`), virtual ATM (`ATM`), convenience store code (`CVS`), barcode (`BARCODE`) and `Alipay`.
9
+
10
+ The gem is published on [RubyGems](https://rubygems.org/gems/active_merchant_ecpay). See [CHANGELOG.md](CHANGELOG.md) for what changed in each version.
11
+
12
+ ## Requirements
13
+
14
+ | | Supported |
15
+ | --- | --- |
16
+ | Ruby | 3.0, 3.1, 3.2, 3.3, 3.4 |
17
+ | Rails (`actionview` / `activesupport`) | 7.0, 7.1, 7.2, 8.0, 8.1 |
18
+
19
+ Rails 7.2 needs Ruby >= 3.1, and Rails 8.0 / 8.1 need Ruby >= 3.2. These are exactly the combinations the CI matrix (`.github/workflows/test.yml`) runs. Other runtime dependencies: `activemerchant ~> 1.50`, `offsite_payments ~> 2`, `money`.
10
20
 
11
21
  ## Installation
12
22
 
13
- Add this line to your application's Gemfile:
23
+ Add this line to your application's `Gemfile`:
14
24
 
15
25
  ```ruby
16
- gem 'active_merchant_ecpay'
26
+ gem 'active_merchant_ecpay', '>= 0.2.0'
17
27
  ```
18
28
 
19
- And then execute:
29
+ Then run:
20
30
 
21
- $ bundle
31
+ ```sh
32
+ bundle install
33
+ ```
22
34
 
23
- Or install it yourself as:
35
+ > Support for Rails 7+ / Ruby 3+ is released as **0.2.0**. If 0.2.0 is not yet visible on RubyGems (check the [CHANGELOG](CHANGELOG.md); 0.1.2 is the last release for older Rails), use the Git source instead:
36
+ > `gem 'active_merchant_ecpay', github: 'superlanding/active_merchant_ecpay'`.
24
37
 
25
- $ gem install active_merchant_ecpay
38
+ ## Configuration
26
39
 
27
- ## Usage
40
+ Get your merchant credentials from ECPay (the [API spec](https://developers.ecpay.com.tw/) describes every parameter). Then create an initializer, e.g. `config/initializers/ecpay.rb`:
41
+
42
+ ```ruby
43
+ OffsitePayments::Integrations::Ecpay.setup do |ecpay|
44
+ if Rails.env.production?
45
+ ecpay.merchant_id = Rails.application.credentials.dig(:ecpay, :merchant_id)
46
+ ecpay.hash_key = Rails.application.credentials.dig(:ecpay, :hash_key)
47
+ ecpay.hash_iv = Rails.application.credentials.dig(:ecpay, :hash_iv)
48
+ else
49
+ # ECPay's public stage (test) account
50
+ ecpay.merchant_id = '2000132'
51
+ ecpay.hash_key = '5294y06JbISpM5x9'
52
+ ecpay.hash_iv = 'v77hoKGq4kWxNNIS'
53
+ end
54
+ end
55
+ ```
56
+
57
+ Choose the environment. The gateway URL depends on `ActiveMerchant::Billing::Base.mode`:
28
58
 
29
- You can get Payment API and SPEC in [Ecpay API](https://www.ecpay.com.tw/Content/files/ecpay_011.pdf).
30
- Then create an initializer, like initializers/ecpay.rb. Add the following configurations depends on your settings.
59
+ | mode | URL |
60
+ | --- | --- |
61
+ | `:production` | `https://payment.ecpay.com.tw/Cashier/AioCheckOut/V5` |
62
+ | `:development` / `:test` | `https://payment-stage.ecpay.com.tw/Cashier/AioCheckOut/V5` (stage) |
31
63
 
32
- ``` ruby
64
+ ```ruby
33
65
  # config/environments/development.rb
34
66
  config.after_initialize do
35
67
  ActiveMerchant::Billing::Base.mode = :development
@@ -41,55 +73,99 @@ config.after_initialize do
41
73
  end
42
74
  ```
43
75
 
44
- ``` ruby
45
- # initializers/ecpay.rb
46
- OffsitePayments::Integrations::Ecpay.setup do |ecpay|
47
- if Rails.env.development?
48
- # default setting for stage test
49
- ecpay.merchant_id = '2000132'
50
- ecpay.hash_key = '5294y06JbISpM5x9'
51
- ecpay.hash_iv = 'v77hoKGq4kWxNNIS'
52
- else
53
- # change to yours
54
- ecpay.merchant_id = '23466XX'
55
- ecpay.hash_key = 'adfasdsfgseweqwg'
56
- ecpay.hash_iv = '15y3ersee34yasdf'
57
- end
58
- end
59
- ```
76
+ ## Usage
60
77
 
61
- Once you’ve configured ActiveMerchantEcpay, you need a checkout form; it looks like:
78
+ ### 1. Send the customer to the payment page
62
79
 
63
- ``` erb
64
- <% payment_service_for @order.id,
65
- @order.user.email,
66
- service: :ecpay,
67
- html: { method: :post } do |service| %>
68
- <% service.merchant_trade_no "#{@order.id}T#{Time.zone.now}" %>
69
- <% service.merchant_trade_date @order.created_at %>
80
+ `payment_service_for` is available in views. Always call `service.encrypted_data` last; it computes `CheckMacValue` from every field set so far (MD5, ECPay's .NET-style URL encoding).
81
+
82
+ ```erb
83
+ <% payment_service_for @order.id,
84
+ @order.user.email,
85
+ service: :ecpay,
86
+ html: { method: :post } do |service| %>
87
+ <% service.merchant_trade_no "#{@order.id}T#{Time.current.to_i}" %> <%# unique, alphanumeric, max 20 chars %>
88
+ <% service.merchant_trade_date @order.created_at %> <%# formatted as yyyy/MM/dd HH:mm:ss %>
70
89
  <% service.total_amount @order.total.to_i %>
71
- <% service.description @order.id %>
72
- <% service.item_name @order.id %>
90
+ <% service.description 'Order description' %>
91
+ <% service.item_name 'Item name' %>
73
92
  <% service.choose_payment OffsitePayments::Integrations::Ecpay::PAYMENT_ATM %>
74
- <% service.return_url root_path %>
75
- <% service.notify_url ecpay_atm_return_url %>
93
+ <% service.return_url root_url %> <%# ClientBackURL: button on the ECPay page back to your site %>
94
+ <% service.notify_url ecpay_notify_url %> <%# ReturnURL: server-to-server payment result %>
76
95
  <% service.encrypted_data %>
77
96
  <%= submit_tag 'Buy!' %>
78
97
  <% end %>
79
98
  ```
80
99
 
100
+ The rendered `<form method="post">` points at the gateway URL above, with hidden inputs `MerchantID`, `PaymentType` (`aio`), `MerchantTradeNo`, `MerchantTradeDate`, `TotalAmount`, `TradeDesc`, `ItemName`, `ChoosePayment`, `ClientBackURL`, `ReturnURL`, `CheckMacValue`.
101
+
102
+ Payment method constants: `PAYMENT_CREDIT_CARD`, `PAYMENT_ATM`, `PAYMENT_CVS`, `PAYMENT_ALIPAY`, `PAYMENT_BARCODE`; sub-payment constants `SUBPAYMENT_ATM_*` (`TAISHIN`, `ESUN`, `HUANAN`, `BOT`, `FUBON`, `CHINATRUST`, `FIRST`) and `SUBPAYMENT_CVS_*` (`CVS`, `OK`, `FAMILY`, `HILIFE`, `IBON`), used with `service.choose_sub_payment`.
103
+
104
+ Other field setters (snake_case → ECPay parameter): `merchant_id` / `account` → `MerchantID`, `merchant_trade_no` / `order` → `MerchantTradeNo`, `total_amount` / `amount` → `TotalAmount`, `notify_url` → `ReturnURL`, `client_back_url` / `return_url` → `ClientBackURL`, `redirect_url` → `OrderResultURL`, `payment_info_url` → `PaymentInfoURL`, `client_redirect_url` / `payment_redirect_url` → `ClientRedirectURL`, `item_url`, `remark`, `need_extra_paid_info`, `devise_source`, `ignore_payment`, `platform_id`, `invoice_mark`, `hold_trade_amt`, `encrypt_type`, `expire_date` (ATM), `store_expire_date`, `desc_1` … `desc_4` (CVS / BARCODE), and the Alipay fields `alipay_item_name`, `alipay_item_counts`, `alipay_item_price`, `email`, `phone_no`, `uder_name` (sic), `expire_time`.
105
+
106
+ To use a different HashKey / HashIV for one form (for example for multiple merchants), call `service.hash_key '...'` and `service.hash_iv '...'` before `service.encrypted_data`.
107
+
108
+ ### 2. Handle the notification
109
+
110
+ Point `notify_url` at a controller action, skip CSRF verification for it, and verify the signature before trusting the data:
111
+
112
+ ```ruby
113
+ class EcpayController < ApplicationController
114
+ skip_before_action :verify_authenticity_token, only: :notify
115
+
116
+ def notify
117
+ notification = OffsitePayments::Integrations::Ecpay.notification(request.raw_post)
118
+
119
+ if notification.checksum_ok? && notification.complete?
120
+ order = Order.find_by!(id: notification.merchant_trade_no.split('T').first)
121
+ # notification.trade_no, notification.trade_amt, notification.payment_type, notification.payment_date ...
122
+ order.mark_as_paid!(trade_no: notification.trade_no) if notification.status
123
+ end
124
+
125
+ render plain: '1|OK' # ECPay expects "1|OK" as the response body
126
+ end
127
+ end
128
+ ```
129
+
130
+ Available helpers on the notification:
131
+
132
+ - `checksum_ok?` / `acknowledge` — verifies `CheckMacValue` with your `HashKey` / `HashIV`. ECPay has no remote verification, so this is the only authenticity check; **always call it**.
133
+ - `status` — `true` when `RtnCode == '1'` (paid). `complete?` — also `true` for the "number issued" codes: `2` (ATM virtual account issued), `10100073` (CVS / BARCODE code issued) and `800` (cash-on-delivery order created). Use `status` to know whether money was actually received.
134
+ - Readers: `rtn_code`, `rtn_msg`, `merchant_id`, `merchant_trade_no` (alias `item_id`), `trade_no` (alias `transaction_id`), `trade_amt`, `gross` (`Money` in TWD), `payment_date`, `payment_type`, `payment_type_charge_fee`, `trade_date`, `simulate_paid`, `check_mac_value`; ATM: `bank_code`, `v_account`, `expire_date`; credit card: `card4no`; CVS: `payment_no`; BARCODE: `barcode1`, `barcode2`, `barcode3`.
135
+ - To verify with a per-merchant key, set `notification.hash_key = '...'` and `notification.hash_iv = '...'` before `checksum_ok?`.
136
+
137
+ ### Known limitations
138
+
139
+ - `Helper.url_encode` does not yet match ECPay's encoding for `<`, newline, carriage return and `&` inside field values (see the TODO in `helper.rb`); avoid them in `description` / `item_name`.
140
+ - Setting `OffsitePayments::Integrations::Ecpay.debug = true` calls `binding.pry` inside `encrypted_data` (needs the `pry` gem); never enable it in production.
141
+
81
142
  ## Development
82
143
 
83
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
144
+ ```sh
145
+ git clone https://github.com/superlanding/active_merchant_ecpay.git
146
+ cd active_merchant_ecpay
147
+ BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle install
148
+ BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle exec rake t # alias of `rake spec`
149
+ ```
84
150
 
85
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
151
+ Pick any gemfile in `gemfiles/` (`rails_7.0` … `rails_8.1`) that matches your Ruby version. The specs are RSpec, live in `spec/`, and block all network access (any `Net::HTTP` connection raises). Set `COVERAGE=1` to run SimpleCov.
86
152
 
87
- ## Contributing
153
+ CI (`.github/workflows/test.yml`) runs the whole Ruby × Rails matrix for every PR and push to `master`.
88
154
 
89
- Bug reports and pull requests are welcome on GitHub at https://github.com/superlanding/active_merchant_ecpay.
155
+ ## Releasing
90
156
 
157
+ Releases are automatic (RubyGems [Trusted Publishing](https://guides.rubygems.org/trusted-publishing/), no API key stored):
91
158
 
92
- ## License
159
+ 1. In your PR, bump `ActiveMerchantEcpay::VERSION` in `lib/active_merchant_ecpay/version.rb` (SemVer) and move the `## [Unreleased]` entries in `CHANGELOG.md` under a new `## [x.y.z] - YYYY-MM-DD` section.
160
+ 2. Merge the PR into `master`.
161
+ 3. `.github/workflows/publish.yml` sees that the version is not on rubygems.org yet, builds the gem and runs `rake release`, which publishes it and creates the `vx.y.z` git tag. If the version already exists, the workflow does nothing.
162
+
163
+ PRs that do not bump the version (docs, CI, refactors) are never published.
164
+
165
+ ## Contributing
166
+
167
+ Bug reports and pull requests are welcome on GitHub at https://github.com/superlanding/active_merchant_ecpay. Every pull request must also update this README (when behaviour, usage or requirements change) and add an entry to `CHANGELOG.md`; see `CLAUDE.md`.
93
168
 
94
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
169
+ ## License
95
170
 
171
+ The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
data/Rakefile CHANGED
@@ -3,4 +3,5 @@ require "rspec/core/rake_task"
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
+ task :t => :spec
6
7
  task :default => :spec
@@ -14,6 +14,8 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/superlanding/active_merchant_ecpay"
15
15
  spec.license = "MIT"
16
16
 
17
+ spec.required_ruby_version = ">= 3.0"
18
+
17
19
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
20
  spec.bindir = "exe"
19
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -23,11 +25,12 @@ Gem::Specification.new do |spec|
23
25
  spec.add_dependency 'money'
24
26
  spec.add_dependency 'offsite_payments', '~> 2'
25
27
 
26
- spec.add_development_dependency "bundler", "~> 1.14"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_dependency 'actionview', '>= 7.0', '< 8.2'
29
+ spec.add_dependency 'activesupport', '>= 7.0', '< 8.2'
30
+
31
+ spec.add_development_dependency "bundler", ">= 2.0"
32
+ spec.add_development_dependency "rake", ">= 13"
28
33
  spec.add_development_dependency "rspec", "~> 3.0"
29
- spec.add_development_dependency('mocha', '~> 0.13.0')
30
- spec.add_development_dependency('rails', '>= 2.3.14')
31
34
  spec.add_development_dependency('pry')
32
35
  spec.add_development_dependency('thor')
33
36
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec path: ".."
5
+
6
+ gem "rails", "~> 7.0.0"
7
+ gem "rake", ">= 13"
8
+ gem "rspec", "~> 3.0"
9
+ gem "simplecov", require: false
10
+
11
+ # concurrent-ruby 1.3.5 起不再自動 require logger,Rails 7.0 / 7.1 會載入失敗
12
+ gem "concurrent-ruby", "< 1.3.5"
13
+
14
+ # Ruby 3.4 已不再內建這些 gem;Rails 7.0 / 7.1 沒有自行宣告,故明確列出
15
+ gem "logger"
16
+ gem "mutex_m"
17
+ gem "bigdecimal"
18
+ gem "base64"
19
+ gem "drb"
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec path: ".."
5
+
6
+ gem "rails", "~> 7.1.0"
7
+ gem "rake", ">= 13"
8
+ gem "rspec", "~> 3.0"
9
+ gem "simplecov", require: false
10
+
11
+ # concurrent-ruby 1.3.5 起不再自動 require logger,Rails 7.0 / 7.1 會載入失敗
12
+ gem "concurrent-ruby", "< 1.3.5"
13
+
14
+ # Ruby 3.4 已不再內建這些 gem;Rails 7.0 / 7.1 沒有自行宣告,故明確列出
15
+ gem "logger"
16
+ gem "mutex_m"
17
+ gem "bigdecimal"
18
+ gem "base64"
19
+ gem "drb"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec path: ".."
5
+
6
+ gem "rails", "~> 7.2.0"
7
+ gem "rake", ">= 13"
8
+ gem "rspec", "~> 3.0"
9
+ gem "simplecov", require: false
10
+
11
+ # Ruby 3.4 已不再內建這些 gem;Rails 7.0 / 7.1 沒有自行宣告,故明確列出
12
+ gem "logger"
13
+ gem "mutex_m"
14
+ gem "bigdecimal"
15
+ gem "base64"
16
+ gem "drb"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec path: ".."
5
+
6
+ gem "rails", "~> 8.0.0"
7
+ gem "rake", ">= 13"
8
+ gem "rspec", "~> 3.0"
9
+ gem "simplecov", require: false
10
+
11
+ # Ruby 3.4 已不再內建這些 gem;Rails 7.0 / 7.1 沒有自行宣告,故明確列出
12
+ gem "logger"
13
+ gem "mutex_m"
14
+ gem "bigdecimal"
15
+ gem "base64"
16
+ gem "drb"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ source "https://rubygems.org"
3
+
4
+ gemspec path: ".."
5
+
6
+ gem "rails", "~> 8.1.0"
7
+ gem "rake", ">= 13"
8
+ gem "rspec", "~> 3.0"
9
+ gem "simplecov", require: false
10
+
11
+ # Ruby 3.4 已不再內建這些 gem;Rails 7.0 / 7.1 沒有自行宣告,故明確列出
12
+ gem "logger"
13
+ gem "mutex_m"
14
+ gem "bigdecimal"
15
+ gem "base64"
16
+ gem "drb"
@@ -1,3 +1,3 @@
1
1
  module ActiveMerchantEcpay
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_merchant_ecpay
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eddie Li
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2018-11-16 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activemerchant
@@ -53,75 +52,87 @@ dependencies:
53
52
  - !ruby/object:Gem::Version
54
53
  version: '2'
55
54
  - !ruby/object:Gem::Dependency
56
- name: bundler
55
+ name: actionview
57
56
  requirement: !ruby/object:Gem::Requirement
58
57
  requirements:
59
- - - "~>"
58
+ - - ">="
60
59
  - !ruby/object:Gem::Version
61
- version: '1.14'
62
- type: :development
60
+ version: '7.0'
61
+ - - "<"
62
+ - !ruby/object:Gem::Version
63
+ version: '8.2'
64
+ type: :runtime
63
65
  prerelease: false
64
66
  version_requirements: !ruby/object:Gem::Requirement
65
67
  requirements:
66
- - - "~>"
68
+ - - ">="
67
69
  - !ruby/object:Gem::Version
68
- version: '1.14'
70
+ version: '7.0'
71
+ - - "<"
72
+ - !ruby/object:Gem::Version
73
+ version: '8.2'
69
74
  - !ruby/object:Gem::Dependency
70
- name: rake
75
+ name: activesupport
71
76
  requirement: !ruby/object:Gem::Requirement
72
77
  requirements:
73
- - - "~>"
78
+ - - ">="
74
79
  - !ruby/object:Gem::Version
75
- version: '10.0'
76
- type: :development
80
+ version: '7.0'
81
+ - - "<"
82
+ - !ruby/object:Gem::Version
83
+ version: '8.2'
84
+ type: :runtime
77
85
  prerelease: false
78
86
  version_requirements: !ruby/object:Gem::Requirement
79
87
  requirements:
80
- - - "~>"
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '7.0'
91
+ - - "<"
81
92
  - !ruby/object:Gem::Version
82
- version: '10.0'
93
+ version: '8.2'
83
94
  - !ruby/object:Gem::Dependency
84
- name: rspec
95
+ name: bundler
85
96
  requirement: !ruby/object:Gem::Requirement
86
97
  requirements:
87
- - - "~>"
98
+ - - ">="
88
99
  - !ruby/object:Gem::Version
89
- version: '3.0'
100
+ version: '2.0'
90
101
  type: :development
91
102
  prerelease: false
92
103
  version_requirements: !ruby/object:Gem::Requirement
93
104
  requirements:
94
- - - "~>"
105
+ - - ">="
95
106
  - !ruby/object:Gem::Version
96
- version: '3.0'
107
+ version: '2.0'
97
108
  - !ruby/object:Gem::Dependency
98
- name: mocha
109
+ name: rake
99
110
  requirement: !ruby/object:Gem::Requirement
100
111
  requirements:
101
- - - "~>"
112
+ - - ">="
102
113
  - !ruby/object:Gem::Version
103
- version: 0.13.0
114
+ version: '13'
104
115
  type: :development
105
116
  prerelease: false
106
117
  version_requirements: !ruby/object:Gem::Requirement
107
118
  requirements:
108
- - - "~>"
119
+ - - ">="
109
120
  - !ruby/object:Gem::Version
110
- version: 0.13.0
121
+ version: '13'
111
122
  - !ruby/object:Gem::Dependency
112
- name: rails
123
+ name: rspec
113
124
  requirement: !ruby/object:Gem::Requirement
114
125
  requirements:
115
- - - ">="
126
+ - - "~>"
116
127
  - !ruby/object:Gem::Version
117
- version: 2.3.14
128
+ version: '3.0'
118
129
  type: :development
119
130
  prerelease: false
120
131
  version_requirements: !ruby/object:Gem::Requirement
121
132
  requirements:
122
- - - ">="
133
+ - - "~>"
123
134
  - !ruby/object:Gem::Version
124
- version: 2.3.14
135
+ version: '3.0'
125
136
  - !ruby/object:Gem::Dependency
126
137
  name: pry
127
138
  requirement: !ruby/object:Gem::Requirement
@@ -157,9 +168,13 @@ executables: []
157
168
  extensions: []
158
169
  extra_rdoc_files: []
159
170
  files:
171
+ - ".github/workflows/publish.yml"
172
+ - ".github/workflows/test.yml"
160
173
  - ".gitignore"
161
174
  - ".rspec"
162
175
  - ".travis.yml"
176
+ - CHANGELOG.md
177
+ - CLAUDE.md
163
178
  - Gemfile
164
179
  - LICENSE.txt
165
180
  - README.md
@@ -167,6 +182,11 @@ files:
167
182
  - active_merchant_ecpay.gemspec
168
183
  - bin/console
169
184
  - bin/setup
185
+ - gemfiles/rails_7.0.gemfile
186
+ - gemfiles/rails_7.1.gemfile
187
+ - gemfiles/rails_7.2.gemfile
188
+ - gemfiles/rails_8.0.gemfile
189
+ - gemfiles/rails_8.1.gemfile
170
190
  - lib/active_merchant_ecpay.rb
171
191
  - lib/active_merchant_ecpay/version.rb
172
192
  - lib/offsite_payments/integrations/ecpay.rb
@@ -176,7 +196,6 @@ homepage: https://github.com/superlanding/active_merchant_ecpay
176
196
  licenses:
177
197
  - MIT
178
198
  metadata: {}
179
- post_install_message:
180
199
  rdoc_options: []
181
200
  require_paths:
182
201
  - lib
@@ -184,16 +203,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
184
203
  requirements:
185
204
  - - ">="
186
205
  - !ruby/object:Gem::Version
187
- version: '0'
206
+ version: '3.0'
188
207
  required_rubygems_version: !ruby/object:Gem::Requirement
189
208
  requirements:
190
209
  - - ">="
191
210
  - !ruby/object:Gem::Version
192
211
  version: '0'
193
212
  requirements: []
194
- rubyforge_project:
195
- rubygems_version: 2.4.8
196
- signing_key:
213
+ rubygems_version: 3.6.9
197
214
  specification_version: 4
198
215
  summary: ActiveMerchant x Ecpay (綠界科技)
199
216
  test_files: []