cetustek 0.3.0 → 0.5.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/CHANGELOG.md +26 -0
- data/README.md +61 -2
- data/lib/cetustek/cancel_allowance.rb +51 -0
- data/lib/cetustek/cancel_invoice.rb +1 -1
- data/lib/cetustek/create_allowance.rb +75 -0
- data/lib/cetustek/create_invoice.rb +1 -1
- data/lib/cetustek/models/allowance_data.rb +28 -0
- data/lib/cetustek/models/invoice_data.rb +30 -2
- data/lib/cetustek/phone_barcode.rb +38 -0
- data/lib/cetustek/queries.rb +59 -0
- data/lib/cetustek/services/invoice_service.rb +3 -2
- data/lib/cetustek/services/invoice_xml_builder.rb +1 -1
- data/lib/cetustek/version.rb +1 -1
- data/lib/cetustek.rb +5 -0
- metadata +6 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2914b87b1c342a5309f39629998f26cf081179b02c064177f44628c0d1853329
|
|
4
|
+
data.tar.gz: e65afdb10fcfa7c358f7eaa7f953169f0265b6f69e56b624b75e5fe4ea1fc61e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5fe69cd1d22f538861b5dde8124133db53f618b059fc6cb5f7ed6cbe85c0f894c6c4aefb7eb1fda410d883a98f4f910f049a5e9aa0687b5108f7a407f16b209c
|
|
7
|
+
data.tar.gz: 0b64ceeeda241d5f7780e8b7ced545f2309657f3dd3dfcce7b7d631658ed6b1144b2481cd510920ed08ce863970e448c65d9ec485950a41c6ea57eddbeff5f14
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.5.0] - 2026-06-30
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- `Cetustek::PayWay` constants for the 付款方式 codes (spec AVM-26-03 Table 4):
|
|
12
|
+
`CASH`/`ATM`/`CREDIT_CARD`/`CVS`/`OTHER`/`E_PAYMENT` plus `APPLE_PAY`, `LINE_PAY`,
|
|
13
|
+
`GOOGLE_PAY`, `JKO_PAY`, `TAIWAN_PAY`, etc. Convenience only — `payment_type` still
|
|
14
|
+
accepts any raw value, so new platform codes need no gem update.
|
|
15
|
+
|
|
16
|
+
## [0.4.0] - 2026-06-29
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `Cetustek::QueryInvoice.query(invoice_number, invoice_year)` — 查詢發票資訊 (§2.4)
|
|
20
|
+
- `Cetustek::QueryInvoiceNumberByOrderId.query(order_id)` — 以訂單編號查發票號碼 (§2.6)
|
|
21
|
+
- `Cetustek::CreateAllowance.new(allowance_data, check_allowance:).execute` — 開立折讓單 (§2.9)
|
|
22
|
+
- `Cetustek::CancelAllowance.new(allowance_number, reason).execute` — 作廢折讓單 (§2.10)
|
|
23
|
+
- `Cetustek::QueryAllowance.query(allowance_number)` — 查詢折讓資料 (§2.11)
|
|
24
|
+
- `Cetustek::PhoneBarcode.valid?(phone_code)` — 手機條碼驗證 (§3.1, HTTP/JSON, not SOAP)
|
|
25
|
+
- `Cetustek::Models::AllowanceData` value object and an optional `unit` on `InvoiceItem`
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- `hastax` is now taken from the order via `InvoiceData.new(hastax:)` (default `1`,
|
|
29
|
+
tax-inclusive) instead of being hardcoded — e.g. tax-exclusive/tax-free purchases
|
|
30
|
+
can send `0`
|
|
31
|
+
- All generated XML is forced to UTF-8 encoding, preventing `Encoding::CompatibilityError`
|
|
32
|
+
from Savon when invoices/allowances contain Chinese text
|
|
33
|
+
|
|
8
34
|
## [0.3.0] - 2026-06-13
|
|
9
35
|
|
|
10
36
|
### Added
|
data/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Cetustek
|
|
2
2
|
|
|
3
|
+
[](https://rubygems.org/gems/cetustek)
|
|
4
|
+
[](https://github.com/7a6163/cetustek/actions/workflows/rspec.yml)
|
|
5
|
+
[](https://codecov.io/gh/7a6163/cetustek)
|
|
6
|
+
|
|
3
7
|
Cetustek is a Ruby gem designed for handling electronic invoice operations, including invoice cancellation. It communicates with the e-invoice system through SOAP Web Services.
|
|
4
8
|
|
|
5
9
|
## Features
|
|
@@ -112,6 +116,24 @@ Cetustek::Models::InvoiceData.new(
|
|
|
112
116
|
)
|
|
113
117
|
```
|
|
114
118
|
|
|
119
|
+
### Payment method (`payment_type` / PayWay)
|
|
120
|
+
|
|
121
|
+
`payment_type` accepts any raw code, or use `Cetustek::PayWay` for readability
|
|
122
|
+
(`payment_type: Cetustek::PayWay::LINE_PAY`):
|
|
123
|
+
|
|
124
|
+
| Constant | Code | | Constant | Code |
|
|
125
|
+
|----------|------|-|----------|------|
|
|
126
|
+
| `CASH` | 1 | | `GOOGLE_PAY` | `G` |
|
|
127
|
+
| `ATM` | 2 | | `JKO_PAY` | `J` |
|
|
128
|
+
| `CREDIT_CARD` | 3 | | `LINE_PAY` | `L` |
|
|
129
|
+
| `CVS` | 4 | | `PI_WALLET` | `P` |
|
|
130
|
+
| `OTHER` | 5 | | `SAMSUNG_PAY` | `S` |
|
|
131
|
+
| `E_PAYMENT` | 6 | | `TAIWAN_PAY` | `T` |
|
|
132
|
+
| `APPLE_PAY` | `A` | | `EASY_WALLET` | `U` |
|
|
133
|
+
| `AFTEE` | `E` | | `PX_PAY` | `W` |
|
|
134
|
+
| | | | `QUAN_PAY` | `X` |
|
|
135
|
+
| | | | `COIN_CARD` | `Z` |
|
|
136
|
+
|
|
115
137
|
### Discounts and fees
|
|
116
138
|
|
|
117
139
|
The gem is a faithful wrapper of the API's invoice detail format, so it has no
|
|
@@ -130,10 +152,47 @@ Cetustek::Models::InvoiceItem.new(code: 'DISCOUNT', name: '折抵', quantity: 1,
|
|
|
130
152
|
Cetustek::CancelInvoice.new(invoice).execute
|
|
131
153
|
```
|
|
132
154
|
|
|
133
|
-
### Query
|
|
155
|
+
### Query invoices
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
Cetustek::QueryInvoiceByOrderId.query(order_id) # by order id
|
|
159
|
+
Cetustek::QueryInvoice.query(invoice_number, invoice_year) # by invoice number + year
|
|
160
|
+
Cetustek::QueryInvoiceNumberByOrderId.query(order_id) # just the invoice number
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Tax-inclusive vs tax-exclusive prices (`hastax`)
|
|
164
|
+
|
|
165
|
+
`hastax` comes from the order, not a fixed value: `1` (default) means the item
|
|
166
|
+
`unit_price`s already include tax; `0` means they are tax-exclusive (e.g. a tax-free
|
|
167
|
+
purchase). Set it on `InvoiceData`:
|
|
168
|
+
|
|
169
|
+
```ruby
|
|
170
|
+
Cetustek::Models::InvoiceData.new(hastax: 0, items: [...])
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Allowances (折讓單)
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
allowance = Cetustek::Models::AllowanceData.new(
|
|
177
|
+
allowance_number: 'AA20240216000001',
|
|
178
|
+
allowance_date: Time.zone.today,
|
|
179
|
+
invoice_number: 'AA10000000',
|
|
180
|
+
invoice_year: '2024',
|
|
181
|
+
tax_type: 1,
|
|
182
|
+
reason: '退回',
|
|
183
|
+
items: [
|
|
184
|
+
Cetustek::Models::InvoiceItem.new(code: '0001', name: '禮券', quantity: 1, unit: '本', unit_price: 800)
|
|
185
|
+
]
|
|
186
|
+
)
|
|
187
|
+
Cetustek::CreateAllowance.new(allowance).execute # => "A0" on success
|
|
188
|
+
Cetustek::CancelAllowance.new('AA20240216000001', '明細錯誤').execute # => "C0" on success
|
|
189
|
+
Cetustek::QueryAllowance.query('AA20240216000001')
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Mobile barcode validation (手機條碼)
|
|
134
193
|
|
|
135
194
|
```ruby
|
|
136
|
-
|
|
195
|
+
Cetustek::PhoneBarcode.valid?('/ABC123') # => true / false
|
|
137
196
|
```
|
|
138
197
|
|
|
139
198
|
## Development
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ox'
|
|
4
|
+
require 'cgi'
|
|
5
|
+
|
|
6
|
+
module Cetustek
|
|
7
|
+
# 2.10 CancelAllowance 作廢折讓單. Returns the result code (C0 = success).
|
|
8
|
+
class CancelAllowance
|
|
9
|
+
def initialize(allowance_number, reason)
|
|
10
|
+
@allowance_number = allowance_number
|
|
11
|
+
@reason = reason
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def execute
|
|
15
|
+
perform
|
|
16
|
+
@response.body[:cancel_allowance_response][:return]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def perform
|
|
22
|
+
client = Savon.client(wsdl: Cetustek.config.url, open_timeout: 300, read_timeout: 300)
|
|
23
|
+
@response = client.call(:cancel_allowance, message: {
|
|
24
|
+
allowancexml: generate_xml,
|
|
25
|
+
source: Cetustek.config.site_id + Cetustek.config.password,
|
|
26
|
+
rentid: Cetustek.config.username
|
|
27
|
+
})
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def generate_xml
|
|
31
|
+
doc = Ox::Document.new
|
|
32
|
+
instruct = Ox::Instruct.new(:xml)
|
|
33
|
+
instruct[:version] = '1.0'
|
|
34
|
+
instruct[:encoding] = 'UTF-8'
|
|
35
|
+
doc << instruct
|
|
36
|
+
|
|
37
|
+
allowance = Ox::Element.new('Allowance')
|
|
38
|
+
allowance[:XSDVersion] = '2.8'
|
|
39
|
+
doc << allowance
|
|
40
|
+
|
|
41
|
+
allowance << raw_tag('AllowanceNumber', @allowance_number)
|
|
42
|
+
allowance << raw_tag('Reason', @reason)
|
|
43
|
+
|
|
44
|
+
Ox.dump(doc).force_encoding('UTF-8')
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def raw_tag(name, value)
|
|
48
|
+
Ox::Raw.new("<#{name}>#{CGI.escapeHTML(value.to_s)}</#{name}>")
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -42,7 +42,7 @@ module Cetustek
|
|
|
42
42
|
invoice << Ox::Raw.new("<InvoiceNumber>#{@invoice.number}</InvoiceNumber>")
|
|
43
43
|
invoice << Ox::Raw.new("<InvoiceYear>#{@invoice.created_at.year}</InvoiceYear>")
|
|
44
44
|
invoice << Ox::Raw.new('<Remark>退貨</Remark>')
|
|
45
|
-
@xml = Ox.dump(doc)
|
|
45
|
+
@xml = Ox.dump(doc).force_encoding('UTF-8')
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def analize_response
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ox'
|
|
4
|
+
require 'cgi'
|
|
5
|
+
|
|
6
|
+
module Cetustek
|
|
7
|
+
# 2.9 CreateAllowance 開立折讓單. Returns the result code (A0 = success).
|
|
8
|
+
# check_allowance: 0 = confirmed allowance (default), 1 = unconfirmed.
|
|
9
|
+
class CreateAllowance
|
|
10
|
+
def initialize(allowance_data, check_allowance: 0)
|
|
11
|
+
@data = allowance_data
|
|
12
|
+
@check_allowance = check_allowance
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def execute
|
|
16
|
+
perform
|
|
17
|
+
@response.body[:create_allowance_response][:return]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def perform
|
|
23
|
+
client = Savon.client(wsdl: Cetustek.config.url, open_timeout: 300, read_timeout: 300)
|
|
24
|
+
@response = client.call(:create_allowance, message: {
|
|
25
|
+
allowancexml: generate_xml,
|
|
26
|
+
checkallowance: @check_allowance,
|
|
27
|
+
source: Cetustek.config.site_id + Cetustek.config.password,
|
|
28
|
+
rentid: Cetustek.config.username
|
|
29
|
+
})
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def generate_xml
|
|
33
|
+
doc = Ox::Document.new
|
|
34
|
+
instruct = Ox::Instruct.new(:xml)
|
|
35
|
+
instruct[:version] = '1.0'
|
|
36
|
+
instruct[:encoding] = 'UTF-8'
|
|
37
|
+
doc << instruct
|
|
38
|
+
|
|
39
|
+
allowance = Ox::Element.new('Allowance')
|
|
40
|
+
allowance[:XSDVersion] = '2.8'
|
|
41
|
+
doc << allowance
|
|
42
|
+
|
|
43
|
+
allowance << raw_tag('AllowanceNumber', @data.allowance_number)
|
|
44
|
+
allowance << raw_tag('AllowanceDate', @data.allowance_date.strftime('%Y/%m/%d'))
|
|
45
|
+
allowance << raw_tag('InvoiceNumber', @data.invoice_number)
|
|
46
|
+
allowance << raw_tag('InvoiceYear', @data.invoice_year)
|
|
47
|
+
allowance << raw_tag('BuyerAddress', @data.buyer_address)
|
|
48
|
+
allowance << raw_tag('BuyerEmailAddress', @data.buyer_email)
|
|
49
|
+
allowance << raw_tag('TaxType', @data.tax_type)
|
|
50
|
+
allowance << raw_tag('Reason', @data.reason)
|
|
51
|
+
allowance << raw_tag('RoundNum', @data.round_num) unless @data.round_num.nil?
|
|
52
|
+
allowance << build_details
|
|
53
|
+
|
|
54
|
+
Ox.dump(doc).force_encoding('UTF-8')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def build_details
|
|
58
|
+
details = Ox::Element.new('Details')
|
|
59
|
+
@data.items.each do |item|
|
|
60
|
+
product = Ox::Element.new('ProductItem')
|
|
61
|
+
product << raw_tag('ProductionCode', item.code)
|
|
62
|
+
product << raw_tag('Description', item.name)
|
|
63
|
+
product << raw_tag('Quantity', item.quantity)
|
|
64
|
+
product << raw_tag('Unit', item.unit)
|
|
65
|
+
product << raw_tag('UnitPrice', item.unit_price)
|
|
66
|
+
details << product
|
|
67
|
+
end
|
|
68
|
+
details
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def raw_tag(name, value)
|
|
72
|
+
Ox::Raw.new("<#{name}>#{CGI.escapeHTML(value.to_s)}</#{name}>")
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -13,7 +13,7 @@ module Cetustek
|
|
|
13
13
|
|
|
14
14
|
def execute
|
|
15
15
|
xml = Services::InvoiceXmlBuilder.new(@invoice_data).build
|
|
16
|
-
response = Services::InvoiceService.new(xml, @invoice_data.order_id).create
|
|
16
|
+
response = Services::InvoiceService.new(xml, @invoice_data.order_id, @invoice_data.hastax).create
|
|
17
17
|
result = Services::ResponseHandler.new(response, @invoice_data, xml).process
|
|
18
18
|
|
|
19
19
|
if defined?(Rails) && result[:number] && result[:random_number]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'invoice_data'
|
|
4
|
+
|
|
5
|
+
module Cetustek
|
|
6
|
+
module Models
|
|
7
|
+
# Request data for CreateAllowance (開立折讓單), spec AVM-26-03 Table 15/16.
|
|
8
|
+
# Line items reuse InvoiceItem (code/name/quantity/unit/unit_price).
|
|
9
|
+
class AllowanceData
|
|
10
|
+
attr_reader :allowance_number, :allowance_date, :invoice_number,
|
|
11
|
+
:invoice_year, :buyer_address, :buyer_email, :tax_type,
|
|
12
|
+
:reason, :round_num, :items
|
|
13
|
+
|
|
14
|
+
def initialize(attributes = {})
|
|
15
|
+
@allowance_number = attributes[:allowance_number]
|
|
16
|
+
@allowance_date = attributes[:allowance_date]
|
|
17
|
+
@invoice_number = attributes[:invoice_number]
|
|
18
|
+
@invoice_year = attributes[:invoice_year]
|
|
19
|
+
@buyer_address = attributes[:buyer_address]
|
|
20
|
+
@buyer_email = attributes[:buyer_email]
|
|
21
|
+
@tax_type = attributes[:tax_type] || TaxType::TAXABLE
|
|
22
|
+
@reason = attributes[:reason]
|
|
23
|
+
@round_num = attributes[:round_num] # optional 金額計算位數
|
|
24
|
+
@items = attributes[:items] || []
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -11,6 +11,30 @@ module Cetustek
|
|
|
11
11
|
MIXED = 9 # 混合(應稅、零稅率與免稅,限收銀機類型發票)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
+
# PayWay (付款方式) codes for CreateInvoiceV3, spec AVM-26-03 Table 4.
|
|
15
|
+
# Codes are mixed: 1-6 are integers, A-Z are strings. Convenience constants
|
|
16
|
+
# only — InvoiceData#payment_type still accepts any raw value.
|
|
17
|
+
module PayWay
|
|
18
|
+
CASH = 1 # 現金
|
|
19
|
+
ATM = 2 # ATM
|
|
20
|
+
CREDIT_CARD = 3 # 信用卡
|
|
21
|
+
CVS = 4 # 超商代收
|
|
22
|
+
OTHER = 5 # 其他
|
|
23
|
+
E_PAYMENT = 6 # 電子支付
|
|
24
|
+
APPLE_PAY = 'A' # Apple Pay
|
|
25
|
+
AFTEE = 'E' # 先享後付 Aftee
|
|
26
|
+
GOOGLE_PAY = 'G' # Google Pay
|
|
27
|
+
JKO_PAY = 'J' # 街口支付
|
|
28
|
+
LINE_PAY = 'L' # Line Pay
|
|
29
|
+
PI_WALLET = 'P' # Pi 拍錢包、慢點付
|
|
30
|
+
SAMSUNG_PAY = 'S' # Samsung Pay
|
|
31
|
+
TAIWAN_PAY = 'T' # 台灣 Pay
|
|
32
|
+
EASY_WALLET = 'U' # 悠遊付
|
|
33
|
+
PX_PAY = 'W' # 全盈+PAY
|
|
34
|
+
QUAN_PAY = 'X' # 全支付
|
|
35
|
+
COIN_CARD = 'Z' # 銀角零卡
|
|
36
|
+
end
|
|
37
|
+
|
|
14
38
|
module Models
|
|
15
39
|
class InvoiceData
|
|
16
40
|
DEFAULT_TAX_RATE = 0.05
|
|
@@ -19,7 +43,7 @@ module Cetustek
|
|
|
19
43
|
attr_reader :order_id, :order_date, :buyer_identifier, :buyer_name,
|
|
20
44
|
:buyer_email, :donate_mark, :carrier_type, :carrier_id,
|
|
21
45
|
:carrier_id2, :npo_ban, :items, :payment_type,
|
|
22
|
-
:tax_type, :tax_rate, :invoice_type
|
|
46
|
+
:tax_type, :tax_rate, :invoice_type, :hastax
|
|
23
47
|
|
|
24
48
|
def initialize(attributes = {})
|
|
25
49
|
@order_id = attributes[:order_id]
|
|
@@ -37,6 +61,9 @@ module Cetustek
|
|
|
37
61
|
@tax_type = attributes[:tax_type] || TaxType::TAXABLE
|
|
38
62
|
@tax_rate = attributes.fetch(:tax_rate, DEFAULT_TAX_RATE)
|
|
39
63
|
@invoice_type = attributes[:invoice_type] || DEFAULT_INVOICE_TYPE
|
|
64
|
+
# hastax: 0 = item prices are tax-exclusive, 1 = tax-inclusive.
|
|
65
|
+
# Comes from the order (e.g. tax-free purchases), not a fixed value.
|
|
66
|
+
@hastax = attributes.fetch(:hastax, 1)
|
|
40
67
|
end
|
|
41
68
|
|
|
42
69
|
# 混合稅率發票 (限收銀機):每筆明細需標註 DType。
|
|
@@ -53,13 +80,14 @@ module Cetustek
|
|
|
53
80
|
tax_free: 'TN' # 免稅商品
|
|
54
81
|
}.freeze
|
|
55
82
|
|
|
56
|
-
attr_reader :code, :name, :quantity, :unit_price, :tax_type
|
|
83
|
+
attr_reader :code, :name, :quantity, :unit_price, :tax_type, :unit
|
|
57
84
|
|
|
58
85
|
def initialize(attributes = {})
|
|
59
86
|
@code = attributes[:code]
|
|
60
87
|
@name = attributes[:name]
|
|
61
88
|
@quantity = attributes[:quantity]
|
|
62
89
|
@unit_price = attributes[:unit_price]
|
|
90
|
+
@unit = attributes[:unit]
|
|
63
91
|
@tax_type = attributes[:tax_type] || :taxable
|
|
64
92
|
end
|
|
65
93
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Cetustek
|
|
8
|
+
# 3.1 手機條碼 API — validates a mobile barcode (手機條碼) against the
|
|
9
|
+
# 財政部 platform. Plain HTTP GET returning JSON, NOT the SOAP service.
|
|
10
|
+
class PhoneBarcode
|
|
11
|
+
ENDPOINT = 'https://api.cetustek.com.tw/PhoneBar.php'
|
|
12
|
+
AUTH_KEY = 'Cetus9Phone1API7' # spec: fixed for now, may become per-account
|
|
13
|
+
|
|
14
|
+
def self.valid?(phone_code)
|
|
15
|
+
new(phone_code).valid?
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(phone_code)
|
|
19
|
+
@phone_code = phone_code
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# true when the barcode exists (isExist == 'Y').
|
|
23
|
+
def valid?
|
|
24
|
+
response['isExist'] == 'Y'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Full parsed JSON response: { "isExist", "code", "msg", "TxID", "version" }.
|
|
28
|
+
def response
|
|
29
|
+
uri = URI(ENDPOINT)
|
|
30
|
+
uri.query = URI.encode_www_form(
|
|
31
|
+
rentid: Cetustek.config.username,
|
|
32
|
+
authkey: AUTH_KEY,
|
|
33
|
+
phonecode: @phone_code
|
|
34
|
+
)
|
|
35
|
+
JSON.parse(Net::HTTP.get_response(uri).body)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cetustek
|
|
4
|
+
# Read-only SOAP queries. Each returns the raw Savon response, mirroring
|
|
5
|
+
# QueryInvoiceByOrderId. Spec AVM-26-03 §2.4 / §2.6 / §2.11.
|
|
6
|
+
module Queries
|
|
7
|
+
def soap_client
|
|
8
|
+
Savon.client(wsdl: Cetustek.config.url, open_timeout: 300, read_timeout: 300)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def source
|
|
12
|
+
Cetustek.config.site_id + Cetustek.config.password
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def rentid
|
|
16
|
+
Cetustek.config.username
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# 2.4 QueryInvoice 查詢發票資訊 (by invoice number + year)
|
|
21
|
+
class QueryInvoice
|
|
22
|
+
extend Queries
|
|
23
|
+
|
|
24
|
+
def self.query(invoice_number, invoice_year)
|
|
25
|
+
soap_client.call(:query_invoice, message: {
|
|
26
|
+
invoicenumber: invoice_number,
|
|
27
|
+
invoiceyear: invoice_year,
|
|
28
|
+
source: source,
|
|
29
|
+
rentid: rentid
|
|
30
|
+
})
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# 2.6 QueryInvoiceNumberbyOrderid 以訂單編號查詢發票號碼
|
|
35
|
+
class QueryInvoiceNumberByOrderId
|
|
36
|
+
extend Queries
|
|
37
|
+
|
|
38
|
+
def self.query(order_id)
|
|
39
|
+
soap_client.call(:query_invoice_number_by_orderid, message: {
|
|
40
|
+
orderid: order_id,
|
|
41
|
+
source: source,
|
|
42
|
+
rentid: rentid
|
|
43
|
+
})
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# 2.11 QueryAllowance 查詢折讓資料
|
|
48
|
+
class QueryAllowance
|
|
49
|
+
extend Queries
|
|
50
|
+
|
|
51
|
+
def self.query(allowance_number)
|
|
52
|
+
soap_client.call(:query_allowance, message: {
|
|
53
|
+
allowancenumber: allowance_number,
|
|
54
|
+
source: source,
|
|
55
|
+
rentid: rentid
|
|
56
|
+
})
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -6,9 +6,10 @@ require 'logger'
|
|
|
6
6
|
module Cetustek
|
|
7
7
|
module Services
|
|
8
8
|
class InvoiceService
|
|
9
|
-
def initialize(xml, order_id = nil)
|
|
9
|
+
def initialize(xml, order_id = nil, hastax = 1)
|
|
10
10
|
@xml = xml
|
|
11
11
|
@order_id = order_id
|
|
12
|
+
@hastax = hastax
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def create
|
|
@@ -33,7 +34,7 @@ module Cetustek
|
|
|
33
34
|
invoicexml: @xml,
|
|
34
35
|
source: Cetustek.config.site_id + Cetustek.config.password,
|
|
35
36
|
rentid: Cetustek.config.username,
|
|
36
|
-
hastax:
|
|
37
|
+
hastax: @hastax
|
|
37
38
|
})
|
|
38
39
|
end
|
|
39
40
|
|
data/lib/cetustek/version.rb
CHANGED
data/lib/cetustek.rb
CHANGED
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "cetustek/version"
|
|
4
4
|
require_relative "cetustek/configuration"
|
|
5
|
+
require_relative "cetustek/models/allowance_data"
|
|
5
6
|
require_relative "cetustek/create_invoice"
|
|
6
7
|
require_relative "cetustek/cancel_invoice"
|
|
7
8
|
require_relative "cetustek/query_invoice_by_order_id"
|
|
9
|
+
require_relative "cetustek/queries"
|
|
10
|
+
require_relative "cetustek/create_allowance"
|
|
11
|
+
require_relative "cetustek/cancel_allowance"
|
|
12
|
+
require_relative "cetustek/phone_barcode"
|
|
8
13
|
|
|
9
14
|
module Cetustek
|
|
10
15
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cetustek
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zac
|
|
@@ -79,10 +79,15 @@ files:
|
|
|
79
79
|
- README.md
|
|
80
80
|
- Rakefile
|
|
81
81
|
- lib/cetustek.rb
|
|
82
|
+
- lib/cetustek/cancel_allowance.rb
|
|
82
83
|
- lib/cetustek/cancel_invoice.rb
|
|
83
84
|
- lib/cetustek/configuration.rb
|
|
85
|
+
- lib/cetustek/create_allowance.rb
|
|
84
86
|
- lib/cetustek/create_invoice.rb
|
|
87
|
+
- lib/cetustek/models/allowance_data.rb
|
|
85
88
|
- lib/cetustek/models/invoice_data.rb
|
|
89
|
+
- lib/cetustek/phone_barcode.rb
|
|
90
|
+
- lib/cetustek/queries.rb
|
|
86
91
|
- lib/cetustek/query_invoice_by_order_id.rb
|
|
87
92
|
- lib/cetustek/services/invoice_service.rb
|
|
88
93
|
- lib/cetustek/services/invoice_xml_builder.rb
|