taiwan-payment-skill 1.0.0 → 1.0.2
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.
- package/README.md +1 -1
- package/assets/taiwan-payment/README.md +197 -306
- package/assets/taiwan-payment/examples/ecpay-payment-example.py +390 -0
- package/assets/taiwan-payment/examples/newebpay-payment-example.py +451 -0
- package/assets/taiwan-payment/examples/payuni-payment-example.py +457 -0
- package/package.json +12 -10
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
PAYUNi 統一金流 Python 完整範例
|
|
4
|
+
|
|
5
|
+
依照 taiwan-payment-skill 最高規範撰寫
|
|
6
|
+
支援: 信用卡、ATM、超商代碼、AFTEE、iCash Pay
|
|
7
|
+
|
|
8
|
+
API 文件: https://www.payuni.com.tw
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import urllib.parse
|
|
13
|
+
import time
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from typing import Dict, Literal, Optional
|
|
16
|
+
from dataclasses import dataclass, field
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
from Crypto.Cipher import AES
|
|
20
|
+
HAS_CRYPTO = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
HAS_CRYPTO = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class PaymentOrderData:
|
|
27
|
+
"""PAYUNi 付款訂單資料"""
|
|
28
|
+
mer_trade_no: str
|
|
29
|
+
trade_amt: int
|
|
30
|
+
prod_desc: str
|
|
31
|
+
return_url: str
|
|
32
|
+
notify_url: str
|
|
33
|
+
pay_type: Literal['Credit', 'VACC', 'CVS', 'AFTEE', 'iCashPay']
|
|
34
|
+
trade_limit_date: Optional[str] = None
|
|
35
|
+
unified_id: Optional[str] = None
|
|
36
|
+
buyer_name: Optional[str] = None
|
|
37
|
+
buyer_tel: Optional[str] = None
|
|
38
|
+
buyer_email: Optional[str] = None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class PaymentOrderResponse:
|
|
43
|
+
"""PAYUNi 付款訂單回應"""
|
|
44
|
+
success: bool
|
|
45
|
+
status: str
|
|
46
|
+
message: str
|
|
47
|
+
mer_trade_no: str
|
|
48
|
+
trade_no: Optional[str] = None
|
|
49
|
+
payment_url: Optional[str] = None
|
|
50
|
+
atm_bank_code: Optional[str] = None
|
|
51
|
+
atm_account: Optional[str] = None
|
|
52
|
+
atm_expire_date: Optional[str] = None
|
|
53
|
+
cvs_code: Optional[str] = None
|
|
54
|
+
cvs_expire_date: Optional[str] = None
|
|
55
|
+
error_code: Optional[str] = None
|
|
56
|
+
raw: Dict = field(default_factory=dict)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass
|
|
60
|
+
class PaymentCallbackData:
|
|
61
|
+
"""PAYUNi 付款回傳資料"""
|
|
62
|
+
status: str
|
|
63
|
+
message: str
|
|
64
|
+
mer_id: str
|
|
65
|
+
mer_trade_no: str
|
|
66
|
+
trade_no: str
|
|
67
|
+
trade_amt: int
|
|
68
|
+
trade_status: str
|
|
69
|
+
pay_type: str
|
|
70
|
+
pay_date: str
|
|
71
|
+
settle_date: Optional[str] = None
|
|
72
|
+
checksum: str
|
|
73
|
+
raw: Dict = field(default_factory=dict)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class PAYUNiPaymentService:
|
|
77
|
+
"""
|
|
78
|
+
PAYUNi 統一金流服務
|
|
79
|
+
|
|
80
|
+
認證方式: AES-256-GCM + SHA256
|
|
81
|
+
加密方式: AES-GCM 加密 + SHA256 驗證
|
|
82
|
+
|
|
83
|
+
支援付款方式:
|
|
84
|
+
- Credit: 信用卡
|
|
85
|
+
- VACC: ATM 轉帳
|
|
86
|
+
- CVS: 超商代碼
|
|
87
|
+
- AFTEE: AFTEE 先享後付
|
|
88
|
+
- iCashPay: iCash Pay
|
|
89
|
+
|
|
90
|
+
測試環境:
|
|
91
|
+
- API URL: https://sandbox-api.payuni.com.tw/api/upp
|
|
92
|
+
- 需至 PAYUNi 申請測試帳號
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
# 測試環境
|
|
96
|
+
TEST_API_URL = 'https://sandbox-api.payuni.com.tw/api/upp'
|
|
97
|
+
TEST_QUERY_URL = 'https://sandbox-api.payuni.com.tw/api/trade_query'
|
|
98
|
+
|
|
99
|
+
# 正式環境
|
|
100
|
+
PROD_API_URL = 'https://api.payuni.com.tw/api/upp'
|
|
101
|
+
PROD_QUERY_URL = 'https://api.payuni.com.tw/api/trade_query'
|
|
102
|
+
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
mer_id: str,
|
|
106
|
+
hash_key: str,
|
|
107
|
+
hash_iv: str,
|
|
108
|
+
is_production: bool = False
|
|
109
|
+
):
|
|
110
|
+
"""
|
|
111
|
+
初始化 PAYUNi 金流服務
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
mer_id: 商店代號
|
|
115
|
+
hash_key: HashKey
|
|
116
|
+
hash_iv: HashIV (16 bytes)
|
|
117
|
+
is_production: 是否為正式環境 (預設 False)
|
|
118
|
+
|
|
119
|
+
Raises:
|
|
120
|
+
ImportError: 缺少 pycryptodome 套件
|
|
121
|
+
"""
|
|
122
|
+
if not HAS_CRYPTO:
|
|
123
|
+
raise ImportError('需要安裝 pycryptodome: pip install pycryptodome')
|
|
124
|
+
|
|
125
|
+
self.mer_id = mer_id
|
|
126
|
+
self.hash_key = hash_key.encode('utf-8')
|
|
127
|
+
self.hash_iv = hash_iv.encode('utf-8')
|
|
128
|
+
self.api_url = self.PROD_API_URL if is_production else self.TEST_API_URL
|
|
129
|
+
self.query_url = self.PROD_QUERY_URL if is_production else self.TEST_QUERY_URL
|
|
130
|
+
|
|
131
|
+
def encrypt_data(self, data: Dict[str, any]) -> str:
|
|
132
|
+
"""
|
|
133
|
+
加密資料 (AES-256-GCM)
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
data: 交易資料字典
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
str: AES-GCM 加密後的 hex 字串 (含 tag)
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> data = {'MerID': 'MS123', 'TradeAmt': 100}
|
|
143
|
+
>>> encrypted = service.encrypt_data(data)
|
|
144
|
+
>>> len(encrypted) > 0
|
|
145
|
+
True
|
|
146
|
+
"""
|
|
147
|
+
# 步驟 1: 轉換為查詢字串
|
|
148
|
+
query_string = urllib.parse.urlencode(data)
|
|
149
|
+
|
|
150
|
+
# 步驟 2: AES-256-GCM 加密
|
|
151
|
+
cipher = AES.new(self.hash_key, AES.MODE_GCM, nonce=self.hash_iv)
|
|
152
|
+
encrypted, tag = cipher.encrypt_and_digest(query_string.encode('utf-8'))
|
|
153
|
+
|
|
154
|
+
# 步驟 3: 組合加密資料和 tag,轉換為 hex
|
|
155
|
+
return (encrypted + tag).hex()
|
|
156
|
+
|
|
157
|
+
def decrypt_data(self, encrypted_data: str) -> Dict[str, any]:
|
|
158
|
+
"""
|
|
159
|
+
解密資料 (AES-256-GCM)
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
encrypted_data: AES-GCM 加密的 hex 字串
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Dict: 解密後的資料字典
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
ValueError: 解密失敗或驗證失敗
|
|
169
|
+
"""
|
|
170
|
+
try:
|
|
171
|
+
# 步驟 1: hex 轉 bytes
|
|
172
|
+
data = bytes.fromhex(encrypted_data)
|
|
173
|
+
|
|
174
|
+
# 步驟 2: 分離加密資料和 tag (最後 16 bytes 為 tag)
|
|
175
|
+
encrypted = data[:-16]
|
|
176
|
+
tag = data[-16:]
|
|
177
|
+
|
|
178
|
+
# 步驟 3: AES-256-GCM 解密並驗證
|
|
179
|
+
decipher = AES.new(self.hash_key, AES.MODE_GCM, nonce=self.hash_iv)
|
|
180
|
+
decrypted = decipher.decrypt_and_verify(encrypted, tag)
|
|
181
|
+
|
|
182
|
+
# 步驟 4: 解析查詢字串
|
|
183
|
+
query_string = decrypted.decode('utf-8')
|
|
184
|
+
params = urllib.parse.parse_qs(query_string)
|
|
185
|
+
|
|
186
|
+
# 步驟 5: 轉換為單值字典
|
|
187
|
+
result = {k: v[0] if len(v) == 1 else v for k, v in params.items()}
|
|
188
|
+
return result
|
|
189
|
+
except Exception as e:
|
|
190
|
+
raise ValueError(f'解密失敗: {str(e)}')
|
|
191
|
+
|
|
192
|
+
def generate_checksum(self, encrypt_info: str) -> str:
|
|
193
|
+
"""
|
|
194
|
+
產生 Checksum (SHA256)
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
encrypt_info: 加密後的資料
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: SHA256 雜湊值 (大寫)
|
|
201
|
+
|
|
202
|
+
Example:
|
|
203
|
+
>>> checksum = service.generate_checksum('abcd1234')
|
|
204
|
+
>>> len(checksum)
|
|
205
|
+
64
|
|
206
|
+
"""
|
|
207
|
+
# 組合字串: EncryptInfo + HashKey + HashIV
|
|
208
|
+
raw = encrypt_info + self.hash_key.decode('utf-8') + self.hash_iv.decode('utf-8')
|
|
209
|
+
|
|
210
|
+
# SHA256 雜湊並轉大寫
|
|
211
|
+
return hashlib.sha256(raw.encode('utf-8')).hexdigest().upper()
|
|
212
|
+
|
|
213
|
+
def verify_checksum(self, encrypt_info: str, checksum: str) -> bool:
|
|
214
|
+
"""
|
|
215
|
+
驗證 Checksum
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
encrypt_info: 加密後的資料
|
|
219
|
+
checksum: 接收到的 Checksum
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
bool: 驗證是否通過
|
|
223
|
+
"""
|
|
224
|
+
calculated_checksum = self.generate_checksum(encrypt_info)
|
|
225
|
+
return calculated_checksum == checksum.upper()
|
|
226
|
+
|
|
227
|
+
def create_order(
|
|
228
|
+
self,
|
|
229
|
+
data: PaymentOrderData,
|
|
230
|
+
) -> PaymentOrderResponse:
|
|
231
|
+
"""
|
|
232
|
+
建立付款訂單
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
data: 付款訂單資料 (PaymentOrderData)
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
PaymentOrderResponse: 付款訂單回應
|
|
239
|
+
|
|
240
|
+
Raises:
|
|
241
|
+
ValueError: 參數驗證失敗
|
|
242
|
+
Exception: API 請求失敗
|
|
243
|
+
|
|
244
|
+
Example:
|
|
245
|
+
>>> order_data = PaymentOrderData(
|
|
246
|
+
... mer_trade_no=f'UNI{int(time.time())}',
|
|
247
|
+
... trade_amt=3000,
|
|
248
|
+
... prod_desc='測試商品',
|
|
249
|
+
... return_url='https://your-site.com/return',
|
|
250
|
+
... notify_url='https://your-site.com/notify',
|
|
251
|
+
... pay_type='Credit',
|
|
252
|
+
... )
|
|
253
|
+
>>> result = service.create_order(order_data)
|
|
254
|
+
>>> print(result.payment_url)
|
|
255
|
+
"""
|
|
256
|
+
# 參數驗證
|
|
257
|
+
if data.trade_amt < 1:
|
|
258
|
+
raise ValueError('金額必須大於 0')
|
|
259
|
+
if len(data.mer_trade_no) > 30:
|
|
260
|
+
raise ValueError('訂單編號不可超過 30 字元')
|
|
261
|
+
|
|
262
|
+
# 準備 API 參數
|
|
263
|
+
trade_data = {
|
|
264
|
+
'MerID': self.mer_id,
|
|
265
|
+
'MerTradeNo': data.mer_trade_no,
|
|
266
|
+
'TradeAmt': data.trade_amt,
|
|
267
|
+
'ProdDesc': data.prod_desc,
|
|
268
|
+
'ReturnURL': data.return_url,
|
|
269
|
+
'NotifyURL': data.notify_url,
|
|
270
|
+
'PayType': data.pay_type,
|
|
271
|
+
'Timestamp': int(time.time()),
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# 可選參數
|
|
275
|
+
if data.trade_limit_date:
|
|
276
|
+
trade_data['TradeLimitDate'] = data.trade_limit_date
|
|
277
|
+
if data.unified_id:
|
|
278
|
+
trade_data['UnifiedID'] = data.unified_id
|
|
279
|
+
if data.buyer_name:
|
|
280
|
+
trade_data['BuyerName'] = data.buyer_name
|
|
281
|
+
if data.buyer_tel:
|
|
282
|
+
trade_data['BuyerTel'] = data.buyer_tel
|
|
283
|
+
if data.buyer_email:
|
|
284
|
+
trade_data['BuyerEmail'] = data.buyer_email
|
|
285
|
+
|
|
286
|
+
# 加密資料
|
|
287
|
+
encrypt_info = self.encrypt_data(trade_data)
|
|
288
|
+
|
|
289
|
+
# 產生 Checksum
|
|
290
|
+
checksum = self.generate_checksum(encrypt_info)
|
|
291
|
+
|
|
292
|
+
# 準備 API 請求
|
|
293
|
+
api_data = {
|
|
294
|
+
'MerID': self.mer_id,
|
|
295
|
+
'Version': '1.0',
|
|
296
|
+
'EncryptInfo': encrypt_info,
|
|
297
|
+
'HashInfo': checksum,
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
# 發送 API 請求
|
|
301
|
+
try:
|
|
302
|
+
import requests
|
|
303
|
+
response = requests.post(
|
|
304
|
+
self.api_url,
|
|
305
|
+
data=api_data,
|
|
306
|
+
timeout=30,
|
|
307
|
+
)
|
|
308
|
+
response.raise_for_status()
|
|
309
|
+
result = response.json()
|
|
310
|
+
except Exception as e:
|
|
311
|
+
raise Exception(f'API 請求失敗: {str(e)}')
|
|
312
|
+
|
|
313
|
+
# 解析回應
|
|
314
|
+
if result.get('Status') != 'SUCCESS':
|
|
315
|
+
return PaymentOrderResponse(
|
|
316
|
+
success=False,
|
|
317
|
+
status=result.get('Status', 'ERROR'),
|
|
318
|
+
message=result.get('Message', '未知錯誤'),
|
|
319
|
+
mer_trade_no=data.mer_trade_no,
|
|
320
|
+
error_code=result.get('ErrCode'),
|
|
321
|
+
raw=result,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# 解密回應資料
|
|
325
|
+
response_encrypt_info = result.get('EncryptInfo', '')
|
|
326
|
+
if response_encrypt_info:
|
|
327
|
+
try:
|
|
328
|
+
decrypted = self.decrypt_data(response_encrypt_info)
|
|
329
|
+
except:
|
|
330
|
+
decrypted = {}
|
|
331
|
+
else:
|
|
332
|
+
decrypted = {}
|
|
333
|
+
|
|
334
|
+
return PaymentOrderResponse(
|
|
335
|
+
success=True,
|
|
336
|
+
status=result.get('Status', ''),
|
|
337
|
+
message=result.get('Message', ''),
|
|
338
|
+
mer_trade_no=data.mer_trade_no,
|
|
339
|
+
trade_no=decrypted.get('TradeNo'),
|
|
340
|
+
payment_url=decrypted.get('PaymentURL'),
|
|
341
|
+
atm_bank_code=decrypted.get('ATMBankCode'),
|
|
342
|
+
atm_account=decrypted.get('ATMAcct'),
|
|
343
|
+
atm_expire_date=decrypted.get('ATMExpireDate'),
|
|
344
|
+
cvs_code=decrypted.get('CVSCode'),
|
|
345
|
+
cvs_expire_date=decrypted.get('CVSExpireDate'),
|
|
346
|
+
raw=result,
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
def parse_callback(self, callback_data: Dict[str, str]) -> PaymentCallbackData:
|
|
350
|
+
"""
|
|
351
|
+
解析付款回傳資料
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
callback_data: POST 回傳的參數字典
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
PaymentCallbackData: 解析後的回傳資料
|
|
358
|
+
|
|
359
|
+
Raises:
|
|
360
|
+
ValueError: Checksum 驗證失敗或解密失敗
|
|
361
|
+
|
|
362
|
+
Example:
|
|
363
|
+
>>> callback = request.form.to_dict()
|
|
364
|
+
>>> result = service.parse_callback(callback)
|
|
365
|
+
>>> if result.status == 'SUCCESS':
|
|
366
|
+
... print(f"付款成功: {result.trade_no}")
|
|
367
|
+
"""
|
|
368
|
+
# 驗證 Checksum
|
|
369
|
+
encrypt_info = callback_data.get('EncryptInfo', '')
|
|
370
|
+
hash_info = callback_data.get('HashInfo', '')
|
|
371
|
+
|
|
372
|
+
if not self.verify_checksum(encrypt_info, hash_info):
|
|
373
|
+
raise ValueError('Checksum 驗證失敗')
|
|
374
|
+
|
|
375
|
+
# 解密資料
|
|
376
|
+
decrypted = self.decrypt_data(encrypt_info)
|
|
377
|
+
|
|
378
|
+
return PaymentCallbackData(
|
|
379
|
+
status=decrypted.get('Status', ''),
|
|
380
|
+
message=decrypted.get('Message', ''),
|
|
381
|
+
mer_id=decrypted.get('MerID', ''),
|
|
382
|
+
mer_trade_no=decrypted.get('MerTradeNo', ''),
|
|
383
|
+
trade_no=decrypted.get('TradeNo', ''),
|
|
384
|
+
trade_amt=int(decrypted.get('TradeAmt', 0)),
|
|
385
|
+
trade_status=decrypted.get('TradeStatus', ''),
|
|
386
|
+
pay_type=decrypted.get('PayType', ''),
|
|
387
|
+
pay_date=decrypted.get('PayDate', ''),
|
|
388
|
+
settle_date=decrypted.get('SettleDate'),
|
|
389
|
+
checksum=hash_info,
|
|
390
|
+
raw=decrypted,
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
# Usage Example
|
|
395
|
+
if __name__ == '__main__':
|
|
396
|
+
print('=' * 60)
|
|
397
|
+
print('PAYUNi 統一金流 - Python 範例')
|
|
398
|
+
print('=' * 60)
|
|
399
|
+
print()
|
|
400
|
+
|
|
401
|
+
# 檢查是否有 pycryptodome
|
|
402
|
+
if not HAS_CRYPTO:
|
|
403
|
+
print('✗ 錯誤: 需要安裝 pycryptodome 套件')
|
|
404
|
+
print(' 請執行: pip install pycryptodome')
|
|
405
|
+
exit(1)
|
|
406
|
+
|
|
407
|
+
# 注意: 需要替換為您的測試帳號
|
|
408
|
+
print('[注意] 請先至 PAYUNi 申請測試帳號')
|
|
409
|
+
print('並將以下參數替換為您的測試環境資訊')
|
|
410
|
+
print()
|
|
411
|
+
|
|
412
|
+
# 初始化服務 (使用測試環境)
|
|
413
|
+
service = PAYUNiPaymentService(
|
|
414
|
+
mer_id='YOUR_MERCHANT_ID', # 請替換為您的商店代號
|
|
415
|
+
hash_key='YOUR_HASH_KEY', # 請替換為您的 HashKey
|
|
416
|
+
hash_iv='YOUR_HASH_IV', # 請替換為您的 HashIV (16 bytes)
|
|
417
|
+
is_production=False,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# 範例: 建立信用卡付款訂單
|
|
421
|
+
print('[範例] 建立信用卡付款訂單')
|
|
422
|
+
print('-' * 60)
|
|
423
|
+
|
|
424
|
+
order_data = PaymentOrderData(
|
|
425
|
+
mer_trade_no=f'UNI{int(time.time())}',
|
|
426
|
+
trade_amt=3000,
|
|
427
|
+
prod_desc='測試商品購買',
|
|
428
|
+
return_url='https://your-site.com/payment/return',
|
|
429
|
+
notify_url='https://your-site.com/payment/notify',
|
|
430
|
+
pay_type='Credit',
|
|
431
|
+
buyer_name='測試買家',
|
|
432
|
+
buyer_email='test@example.com',
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
try:
|
|
436
|
+
result = service.create_order(order_data)
|
|
437
|
+
|
|
438
|
+
if result.success:
|
|
439
|
+
print(f'✓ 訂單建立成功')
|
|
440
|
+
print(f' 訂單編號: {result.mer_trade_no}')
|
|
441
|
+
print(f' 交易編號: {result.trade_no}')
|
|
442
|
+
print(f' 付款網址: {result.payment_url}')
|
|
443
|
+
print()
|
|
444
|
+
print('請將買家導向付款網址完成付款')
|
|
445
|
+
else:
|
|
446
|
+
print(f'✗ 訂單建立失敗')
|
|
447
|
+
print(f' 狀態: {result.status}')
|
|
448
|
+
print(f' 訊息: {result.message}')
|
|
449
|
+
if result.error_code:
|
|
450
|
+
print(f' 錯誤碼: {result.error_code}')
|
|
451
|
+
except Exception as e:
|
|
452
|
+
print(f'✗ 發生例外: {str(e)}')
|
|
453
|
+
|
|
454
|
+
print()
|
|
455
|
+
print('=' * 60)
|
|
456
|
+
print('範例執行完成')
|
|
457
|
+
print('=' * 60)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "taiwan-payment-skill",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "AI-powered Taiwan Payment Gateway integration toolkit for Claude Code and other AI coding assistants",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"taiwan",
|
|
@@ -32,25 +32,27 @@
|
|
|
32
32
|
},
|
|
33
33
|
"repository": {
|
|
34
34
|
"type": "git",
|
|
35
|
-
"url": "https://github.com/Moksa1123/taiwan-
|
|
35
|
+
"url": "git+https://github.com/Moksa1123/taiwan-invoice.git"
|
|
36
36
|
},
|
|
37
37
|
"bugs": {
|
|
38
|
-
"url": "https://github.com/Moksa1123/taiwan-
|
|
38
|
+
"url": "https://github.com/Moksa1123/taiwan-invoice/issues"
|
|
39
39
|
},
|
|
40
|
-
"homepage": "https://github.com/Moksa1123/taiwan-
|
|
40
|
+
"homepage": "https://github.com/Moksa1123/taiwan-invoice#readme",
|
|
41
41
|
"author": "Moksa1123",
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"commander": "^12.0.0",
|
|
45
44
|
"chalk": "^4.1.2",
|
|
45
|
+
"cli-progress": "^3.12.0",
|
|
46
|
+
"commander": "^11.1.0",
|
|
46
47
|
"ora": "^5.4.1",
|
|
47
|
-
"prompts": "^2.4.2"
|
|
48
|
-
"cli-progress": "^3.12.0"
|
|
48
|
+
"prompts": "^2.4.2"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
|
-
"@types/
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"@types/cli-progress": "^3.11.6",
|
|
52
|
+
"@types/node": "^22.10.1",
|
|
53
|
+
"@types/prompts": "^2.4.9",
|
|
54
|
+
"esbuild": "^0.24.0",
|
|
55
|
+
"typescript": "^5.7.2"
|
|
54
56
|
},
|
|
55
57
|
"engines": {
|
|
56
58
|
"node": ">=18.0.0"
|