特約商店 定期定額獨立頁面
修改歷程
版本 | 異動日期 | 修訂內容 | 位置 |
---|---|---|---|
1.0 | 2024.03.05 | 新版文件 |
開發前請先閱讀
金鑰的注意事項
Q:金鑰的有效期限
A:MyPay的金鑰有效期限只有一年,如果要延長,請自行至金鑰管理系統延長
Q:金鑰可以在什麼時候延長
A:到期日7天前,都可以延長(不包含第7天),一但超過,系統自動產生新金鑰後,即不可再延長
Q:金鑰即將到期前7天時,系統新發的金鑰是否會於信件中夾帶
A:會
Q:金鑰在到期前何時會發信通知
A:定期發送金鑰到期通知的頻率為:60天/30天/20天,若 貴司皆無動作,則系統會於到期前七天產製新金鑰並發送email到 貴司技術窗口信箱,此時舊的金鑰,因還未過期還可以使用,一旦過期,貴司仍使用舊金鑰則會無法交易
請求結果的告知
Q:何時會將建立、修改的結果告知
A:當操作者成功建立、修改或者當網址超過15分鐘沒有觸發延長,會由附錄三 這邊的設定,背景主動發動通知商家
Q:當我接到MyPay通知時該做什麼
A:請直接回應8888,如沒有回應,MyPay會通知5次
定期定額獨立頁面設計概要
安全性設計
每次請求新增或修改的網址僅存在15分鐘,並且開始頁面時必須輸入請求店家的統編,以確認資料的安全性
定期定額 目前提供之服務與格式
定期定額 提供應用方式,應用情境如下:
請求新增網址:透過MyPay提供的定期定額頁面,新增一筆定期定額扣款資料
請求修改網址:透過MyPay提供的定期定額頁面,修改一筆定期定額扣款資料
我們提供介接方式是透過https連線,只接受POST方式傳送交易資料。
介接網址
特約商店模式
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/init |
正式區 | https://ka.mypay.tw/api/init |
Client模式(限定功能)
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/open |
正式區 | https://ka.mypay.tw/api/open |
資料加密方式
AES 256編碼 + base64編碼(附錄四資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
請求建立定期定額頁面
<?php
final class StoreBatchDebitCreator
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "5p8LnrEiuwUn8Zn5yH31s0mg3Jjq5elB";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/batchdebitcreator'
);
}
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['project_name'] = "自訂扣款名目" . date("Ymd");
$rawData['regular'] = "M";
$rawData['order_id'] = "T" . time();
$rawData['group_id'] = "A" . time();
return $rawData;
}
}
$StoreBatchDebitCreator = new StoreBatchDebitCreator();
$StoreBatchDebitCreator->run();
?>
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Dynamic;
/// <summary>
/// 建議使用.net framework4.5以上版本
/// 1.請透過NuGet安裝Newtonsoft.Json,若.net framework小於4.5請安裝Newtonsoft.Json 7.0以下版本
/// 2.若貴司.net framework小於4 可能無法使用dynamic,若是自行組陣列
/// 3.若貴司.net framework小於3.5 可能無法使用AES類別,若是參閱微軟網站使用較舊方式進行加密
/// 4.若貴司.net framework小於3.5 可能沒有Linq可使用,故data只能組字串,Newtonsoft.Json也可能無法使用
/// </summary>
namespace MyPay {
/// <summary>
/// 特約商店串接-定期定額請求建立網址
/// </summary>
public class StoreBatchDebitCreator {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreBatchDebitCreator simulator = new StoreBatchDebitCreator();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
datePart = DateTime.Now.ToString("yyyyMMdd");
DateTime now = DateTime.Now;
long unixTimestamp = ((DateTimeOffset)now).ToUnixTimeSeconds();
string result = string.Concat("A", unixTimestamp);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.project_name = string.Concat("自訂扣款名目", datePart);
rawData.regular = 'M';
rawData.order_id = string.Concat("A", unixTimestamp);
rawData.group_id = string.Concat("T", unixTimestamp);
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/batchdebitcreator";
return rawData;
}
/// <summary>
/// 取得送出欄位資料
/// </summary>
private NameValueCollection GetPostData() {
string data_json = JsonConvert.SerializeObject(GetRawData(), Formatting.None);
string svr_json = JsonConvert.SerializeObject(GetService(), Formatting.None);; //依API種類調整
//產生AES向量
var IV = GetBytesIV();
//進行加密
var data_encode = Encrypt(data_json, this.storeKey, IV);
var svr_encode = Encrypt(svr_json, this.storeKey, IV);
//請注意使用的 Http Post 套件是否會自動加上UrlEncode,本Post範例為原始方式,故須加上UrlEncode
//若自行使用的套件會自動補上UrlEncode,則請忽略下面的UrlEncode,避免做了兩次UrlEncode
string data_toUrlEncode = HttpUtility.UrlEncode(data_encode);
string svr_toUrlEncode = HttpUtility.UrlEncode(svr_encode);
NameValueCollection postData = new NameValueCollection();
postData["store_uid"] = this.storeUid;
postData["service"] = svr_toUrlEncode;
postData["encry_data"] = data_toUrlEncode;
return postData;
}
/// <summary>
/// AES 256 加密
/// </summary>
/// <param name="data"></param>
/// <param name="key"></param>
/// <param name="byteIV"></param>
/// <returns></returns>
private string Encrypt(string data, string key, byte[] byteIV) {
var byteKey = System.Text.Encoding.UTF8.GetBytes(key);
var enBytes = AES_Encrypt(data, byteKey, byteIV);
return Convert.ToBase64String(BytesAdd(byteIV, enBytes));
}
/// <summary>
/// AES 256 加密處理
/// </summary>
/// <param name="original"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
private byte[] AES_Encrypt(string original, byte[] key, byte[] iv) {
try {
var data = Encoding.UTF8.GetBytes(original);
var cipher = Aes.Create().CreateEncryptor(key, iv);
var de = cipher.TransformFinalBlock(data, 0, data.Length);
return de;
} catch {
return null;
}
}
/// <summary>
/// 轉換Bytes
/// </summary>
/// <param name="a"></param>
/// <param name="arryB"></param>
/// <returns></returns>
private byte[] BytesAdd(byte[] a, params byte[][] arryB) {
List < byte > c = new List < byte > ();
c.AddRange(a);
arryB.ToList().ForEach(b => {
c.AddRange(b);
});
return c.ToArray();
}
/// <summary>
/// 產生AES的IV
/// </summary>
/// <returns></returns>
private static byte[] GetBytesIV() {
var aes = System.Security.Cryptography.AesCryptoServiceProvider.Create();
aes.KeySize = 256;
aes.GenerateIV();
return aes.IV;
}
/// <summary>
/// 資料 POST 到主機
/// </summary>
/// <param name="pars"></param>
/// <returns></returns>
private string Post(NameValueCollection pars) {
string result = string.Empty;
string param = string.Empty;
if (pars.Count > 0) {
pars.AllKeys.ToList().ForEach(key => {
param += key + "=" + pars[key] + "&";
});
if (param[param.Length - 1] == '&') {
param = param.Remove(param.Length - 1);
}
}
byte[] bs = Encoding.UTF8.GetBytes(param);
try {
HttpWebRequest req = (HttpWebRequest) HttpWebRequest.Create(this.url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = bs.Length;
using(Stream reqStream = req.GetRequestStream()) {
reqStream.Write(bs, 0, bs.Length);
}
using(WebResponse wr = req.GetResponse()) {
Encoding myEncoding = Encoding.GetEncoding("UTF-8");
using(StreamReader myStreamReader = new StreamReader(wr.GetResponseStream(), myEncoding)) {
result = myStreamReader.ReadToEnd();
}
}
req = null;
} catch (WebException ex) {
throw new WebException(ex.Message + "params : " + param, ex, ex.Status, ex.Response);
}
return result;
}
}
/// <summary>
/// 串接服務請求欄位
/// </summary>
public class ServiceRequest {
public string service_name { get; set; }
public string cmd { get; set; }
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URLEncoder;
import java.util.*;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import java.security.SecureRandom;
/**
* 特約商店串接-定期定額請求建立網址
* 1. jackson-core 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
* 2. jackson-databind 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
* 3. jackson-annotations 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
* 4. Spongy Castle 下載 https://mvnrepository.com/artifact/com.madgag.spongycastle/core
*/
public class StoreBatchDebitCreator {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreBatchDebitCreator simulator = new StoreBatchDebitCreator();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String currentDate = LocalDateTime.now().format(formatter);
rawData.put("store_uid", this.storeUid);
rawData.put("project_name", "自訂扣款名目" + currentDate);
rawData.put("regular", "M");
rawData.put("order_id", "T" + System.currentTimeMillis() / 1000);
rawData.put("group_id", "A" + System.currentTimeMillis() / 1000);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/batchdebitcreator");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-定期定額請求建立網址
*/
function StoreBatchDebitCreator() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreBatchDebitCreator.prototype.getRawData = function () {
const currentDate = new Date().toISOString().slice(0, 10).replace(/-/g, '');
return {
store_uid: this.storeUid,
project_name: "自訂扣款名目" + currentDate,
regular: "M",
order_id: "T" + Math.floor(Date.now() / 1000),
group_id: "A" + Math.floor(Date.now() / 1000)
};
};
/**
* 取得服務位置
*/
StoreBatchDebitCreator.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/batchdebitcreator"
};
};
/**
* AES 256 加密
*/
StoreBatchDebitCreator.prototype.encrypt = function (fields, key) {
let eData = JSON.stringify(fields);
const blockSize = 16;
const iv = crypto.randomBytes(blockSize);
const encryptor = crypto.createCipheriv('aes-256-cbc', key, iv);
let tmpCipher = encryptor.update(Buffer.from(eData));
let finalCipher = encryptor.final();
const tempData = Buffer.concat([tmpCipher, finalCipher], tmpCipher.length + finalCipher.length);
let data = Buffer.concat([iv, tempData], iv.length + tempData.length).toString('base64');
return data;
};
/**
* 資料 POST 到主機
*/
StoreBatchDebitCreator.prototype.post = function (postData) {
return new Promise((res, rej) => {
let options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
rejectUnauthorized: false
};
let send_process = httpRequest.request(this.url, options, (api_res) => {
let res_data = "";
api_res.on('data', (tmp_data) => {
res_data += tmp_data;
});
api_res.on('end', () => {
res(res_data);
});
});
send_process.write(JSON.stringify(postData));
send_process.end();
});
};
/**
* 取得送出欄位資料
*/
StoreBatchDebitCreator.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreBatchDebitCreator.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreBatchDebitCreator = new StoreBatchDebitCreator();
StoreBatchDebitCreator.run();
# -*- coding: utf-8 -*-
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-定期定額請求建立網址
"""
class StoreBatchDebitCreator:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
current_date = datetime.now().strftime('%Y%m%d')
rawData = {
'store_uid': self.storeUid,
'project_name': "自訂扣款名目" + current_date,
'regular': "M",
'order_id': "T" + str(int(time.time())),
'group_id': "A" + str(int(time.time())),
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/batchdebitcreator'
}
def encrypt(self, fields, key):
"""AES 256 加密
Args:
fields {dict}: 欄位資料
key {bytes}: AES金鑰
Returns:
{string}: 加密資料
"""
data = json.dumps(fields, separators=(',', ':'))
data = Padding.pad(data.encode('utf-8'), AES.block_size)
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.encrypt(data)
data = base64.b64encode(iv + data)
return data
def post(self, postData):
"""資料 POST 到主機
Args:
postData {dict}: 欄位資料
Returns:
{string}: JSON資料
"""
result = requests.post(self.url, postData)
return result.text
def getPostData(self):
"""取得送出欄位資料
Returns:
{dict}: 欄位資料
"""
postData = {
'store_uid': self.storeUid,
'service': self.encrypt(self.getService(), self.storeKey),
'encry_data': self.encrypt(self.getRawData(), self.storeKey)
}
return postData
def run(self):
"""執行
"""
json = self.post(self.getPostData())
print(json)
StoreBatchDebitCreator = StoreBatchDebitCreator()
StoreBatchDebitCreator.run()
回傳 JSON 結構如下:
{
"code": "200",
"msg": "資料正確",
"page_code": "6d5924df99",
"url": "http:\/\/pay.k20-mypay.tw\/regularinstallment\/link\/6d5924df99.html"
}
在請求返回後,網址只能存在15分鐘,並且只能於同一個裝置同一瀏覽器開啟,一旦更換瀏覽器或裝置開啟,將會顯示頁面不存在,一旦新增完畢後,系統將會背景通知設定結果
特約商店『請求建立定期定額頁面』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name":"api","cmd":"api\/batchdebitcreator"} JSON格式,AES256加密資料 |
encry_data | text | 『請求建立定期定額頁面』欄位參考 JSON格式,AES256加密資料 |
『請求建立定期定額頁面』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | 必填 |
project_name | string | 專案名稱 | 必填 |
regular | string | 扣款單位 | 必填 『分期類型定義』值參考 |
notify_before | string | 扣款前通知 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 『扣款通知』值參考 |
notify_before_message | string | 通知內容 | 專案名稱若已存在,則此蘭位於新增時無效 |
notify_after | string | 扣款後通知 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 『扣款通知』值參考 |
notify_after_message | string | 通知內容 | |
retry_enable | int | 扣款失敗後,是否啟用重扣機制 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 『扣款失敗後,重扣是否啟用』值參考 |
fail_notify_delay | string | 扣款失敗,延遲幾日後重扣,單位:日 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 僅能輸入3~5 |
retry_time | string | 扣款失敗後,要重試幾次重扣 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 僅能輸入1~2 |
order_id | string | 請求編號(請求編號最長為50bytes),不可重複 | 必填 |
group_id | string | 扣款單號(不可重複,每當交易回報時,會帶入此單號,使之識別扣款為同一筆申請的定期定額) | 必填 |
pay_mode_uid | integer | 付款方式 | 非必填 『卡片類型定義』值參考 |
name | string | 扣款人姓名 | 非必填 |
debtor_type | integer | 扣款人身份類型 | 非必填 『證號類型』值參考 |
debtor_id | string | 扣款人證號 | 非必填 |
phone_code | string | 扣款人手機國碼 | 非必填 |
phone | string | 扣款人手機 | |
string | 扣款人電子信箱 | ||
store_member_id | string | 網站會員ID | |
currency | string | 幣別(目前強制為TWD) | |
cost | string | 每期應扣金額 | 非必填 |
regular_first_charge_date | string | 起扣日期 | 非必填 |
regular_total | string | 扣款次數 | 非必填 |
invoice_type | integer | 發票類型(預設0) | 非必填 『單據類型』值參考 |
cloud_type | int | 雲端載具類型(預設0) | 非必填 『雲端發票類型』值參考 |
invoice_mobile_code | string | 當cloud_type為2時紀錄的手機條碼 | |
invoice_natural_person | string | 當cloud_type為3時紀錄的自然人憑證條碼 | |
invoice_love_code | string | 當input_type為2時紀錄的愛心碼 | |
tong_bian | string | 發票統編 | 非必填 |
donation_statistics_type | int | 捐款收據統計模式(預設0),當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款收據統計模式』值參考 |
donation_identity_type | int | 捐款收據收件對象當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款單位』值參考 |
donation_type_no | string | 捐款收據對象身分證或統編,當donation_identity_type為1時填身分證2填統編,當donation_identity_type為1時必填 | |
donation_type_name | string | 捐款收據對象姓名或公司名稱,當donation_identity_type為1時填姓名2填公司名稱,當donation_identity_type為1時必填 | |
invoice_receipt_zip_code | string | 捐款收據地址郵政區碼 | |
invoice_receipt_city | string | 捐款收據地址縣市 | |
invoice_receipt_district | string | 捐款收據地址鄉區 | |
invoice_receipt_address | string | 捐款收據地址 | |
echo_0 | string | 自訂回傳1 | 非必填 |
echo_1 | string | 自訂回傳2 | 非必填 |
echo_2 | string | 自訂回傳3 | 非必填 |
echo_3 | string | 自訂回傳4 | 非必填 |
echo_4 | string | 自訂回傳5 | 非必填 |
『請求建立定期定額頁面』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回應代碼 | 『執行結果代碼』值參考 |
msg | string | 回應說明 | |
page_code | string | 頁面代碼 | |
url | string | 頁面網址 |
請求修改定期定額網址
<?php
final class StoreBatchDebitUpdater
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "5p8LnrEiuwUn8Zn5yH31s0mg3Jjq5elB";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/batchdebitupdater'
);
}
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['original_order_id'] = "T1709533895";
$rawData['order_id'] = "T" . time();
return $rawData;
}
}
$StoreBatchDebitUpdater = new StoreBatchDebitUpdater();
$StoreBatchDebitUpdater->run();
?>
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Dynamic;
/// <summary>
/// 建議使用.net framework4.5以上版本
/// 1.請透過NuGet安裝Newtonsoft.Json,若.net framework小於4.5請安裝Newtonsoft.Json 7.0以下版本
/// 2.若貴司.net framework小於4 可能無法使用dynamic,若是自行組陣列
/// 3.若貴司.net framework小於3.5 可能無法使用AES類別,若是參閱微軟網站使用較舊方式進行加密
/// 4.若貴司.net framework小於3.5 可能沒有Linq可使用,故data只能組字串,Newtonsoft.Json也可能無法使用
/// </summary>
namespace MyPay {
/// <summary>
/// 特約商店串接-定期定額請求修改網址
/// </summary>
public class StoreBatchDebitUpdater {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreBatchDebitUpdater simulator = new StoreBatchDebitUpdater();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
datePart = DateTime.Now.ToString("yyyyMMdd");
DateTime now = DateTime.Now;
long unixTimestamp = ((DateTimeOffset)now).ToUnixTimeSeconds();
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.original_order_id = "T1709533895";
rawData.order_id = string.Concat("A", unixTimestamp);
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/batchdebitupdater";
return rawData;
}
/// <summary>
/// 取得送出欄位資料
/// </summary>
private NameValueCollection GetPostData() {
string data_json = JsonConvert.SerializeObject(GetRawData(), Formatting.None);
string svr_json = JsonConvert.SerializeObject(GetService(), Formatting.None);; //依API種類調整
//產生AES向量
var IV = GetBytesIV();
//進行加密
var data_encode = Encrypt(data_json, this.storeKey, IV);
var svr_encode = Encrypt(svr_json, this.storeKey, IV);
//請注意使用的 Http Post 套件是否會自動加上UrlEncode,本Post範例為原始方式,故須加上UrlEncode
//若自行使用的套件會自動補上UrlEncode,則請忽略下面的UrlEncode,避免做了兩次UrlEncode
string data_toUrlEncode = HttpUtility.UrlEncode(data_encode);
string svr_toUrlEncode = HttpUtility.UrlEncode(svr_encode);
NameValueCollection postData = new NameValueCollection();
postData["store_uid"] = this.storeUid;
postData["service"] = svr_toUrlEncode;
postData["encry_data"] = data_toUrlEncode;
return postData;
}
/// <summary>
/// AES 256 加密
/// </summary>
/// <param name="data"></param>
/// <param name="key"></param>
/// <param name="byteIV"></param>
/// <returns></returns>
private string Encrypt(string data, string key, byte[] byteIV) {
var byteKey = System.Text.Encoding.UTF8.GetBytes(key);
var enBytes = AES_Encrypt(data, byteKey, byteIV);
return Convert.ToBase64String(BytesAdd(byteIV, enBytes));
}
/// <summary>
/// AES 256 加密處理
/// </summary>
/// <param name="original"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
private byte[] AES_Encrypt(string original, byte[] key, byte[] iv) {
try {
var data = Encoding.UTF8.GetBytes(original);
var cipher = Aes.Create().CreateEncryptor(key, iv);
var de = cipher.TransformFinalBlock(data, 0, data.Length);
return de;
} catch {
return null;
}
}
/// <summary>
/// 轉換Bytes
/// </summary>
/// <param name="a"></param>
/// <param name="arryB"></param>
/// <returns></returns>
private byte[] BytesAdd(byte[] a, params byte[][] arryB) {
List < byte > c = new List < byte > ();
c.AddRange(a);
arryB.ToList().ForEach(b => {
c.AddRange(b);
});
return c.ToArray();
}
/// <summary>
/// 產生AES的IV
/// </summary>
/// <returns></returns>
private static byte[] GetBytesIV() {
var aes = System.Security.Cryptography.AesCryptoServiceProvider.Create();
aes.KeySize = 256;
aes.GenerateIV();
return aes.IV;
}
/// <summary>
/// 資料 POST 到主機
/// </summary>
/// <param name="pars"></param>
/// <returns></returns>
private string Post(NameValueCollection pars) {
string result = string.Empty;
string param = string.Empty;
if (pars.Count > 0) {
pars.AllKeys.ToList().ForEach(key => {
param += key + "=" + pars[key] + "&";
});
if (param[param.Length - 1] == '&') {
param = param.Remove(param.Length - 1);
}
}
byte[] bs = Encoding.UTF8.GetBytes(param);
try {
HttpWebRequest req = (HttpWebRequest) HttpWebRequest.Create(this.url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = bs.Length;
using(Stream reqStream = req.GetRequestStream()) {
reqStream.Write(bs, 0, bs.Length);
}
using(WebResponse wr = req.GetResponse()) {
Encoding myEncoding = Encoding.GetEncoding("UTF-8");
using(StreamReader myStreamReader = new StreamReader(wr.GetResponseStream(), myEncoding)) {
result = myStreamReader.ReadToEnd();
}
}
req = null;
} catch (WebException ex) {
throw new WebException(ex.Message + "params : " + param, ex, ex.Status, ex.Response);
}
return result;
}
}
/// <summary>
/// 串接服務請求欄位
/// </summary>
public class ServiceRequest {
public string service_name { get; set; }
public string cmd { get; set; }
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URLEncoder;
import java.util.*;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import java.security.SecureRandom;
/**
* 特約商店串接-定期定額請求修改網址
* 1. jackson-core 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
* 2. jackson-databind 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
* 3. jackson-annotations 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
* 4. Spongy Castle 下載 https://mvnrepository.com/artifact/com.madgag.spongycastle/core
*/
public class StoreBatchDebitCreator {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreBatchDebitCreator simulator = new StoreBatchDebitCreator();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String currentDate = LocalDateTime.now().format(formatter);
rawData.put("store_uid", this.storeUid);
rawData.put("original_order_id", "T1709533895");
rawData.put("order_id", "T" + System.currentTimeMillis() / 1000);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/batchdebitupdater");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-定期定額請求修改網址
*/
function StoreBatchDebitUpdater() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreBatchDebitUpdater.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
original_order_id: "T1709533895",
order_id: "T" + Math.floor(Date.now() / 1000),
};
};
/**
* 取得服務位置
*/
StoreBatchDebitUpdater.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/batchdebitupdater"
};
};
/**
* AES 256 加密
*/
StoreBatchDebitUpdater.prototype.encrypt = function (fields, key) {
let eData = JSON.stringify(fields);
const blockSize = 16;
const iv = crypto.randomBytes(blockSize);
const encryptor = crypto.createCipheriv('aes-256-cbc', key, iv);
let tmpCipher = encryptor.update(Buffer.from(eData));
let finalCipher = encryptor.final();
const tempData = Buffer.concat([tmpCipher, finalCipher], tmpCipher.length + finalCipher.length);
let data = Buffer.concat([iv, tempData], iv.length + tempData.length).toString('base64');
return data;
};
/**
* 資料 POST 到主機
*/
StoreBatchDebitUpdater.prototype.post = function (postData) {
return new Promise((res, rej) => {
let options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
rejectUnauthorized: false
};
let send_process = httpRequest.request(this.url, options, (api_res) => {
let res_data = "";
api_res.on('data', (tmp_data) => {
res_data += tmp_data;
});
api_res.on('end', () => {
res(res_data);
});
});
send_process.write(JSON.stringify(postData));
send_process.end();
});
};
/**
* 取得送出欄位資料
*/
StoreBatchDebitUpdater.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreBatchDebitUpdater.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreBatchDebitUpdater = new StoreBatchDebitUpdater();
StoreBatchDebitUpdater.run();
# -*- coding: utf-8 -*-
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-定期定額請求建立網址
"""
class StoreBatchDebitUpdater:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'original_order_id': "T1709533895",
'order_id': "T" + str(int(time.time()))
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/batchdebitupdater'
}
def encrypt(self, fields, key):
"""AES 256 加密
Args:
fields {dict}: 欄位資料
key {bytes}: AES金鑰
Returns:
{string}: 加密資料
"""
data = json.dumps(fields, separators=(',', ':'))
data = Padding.pad(data.encode('utf-8'), AES.block_size)
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.encrypt(data)
data = base64.b64encode(iv + data)
return data
def post(self, postData):
"""資料 POST 到主機
Args:
postData {dict}: 欄位資料
Returns:
{string}: JSON資料
"""
result = requests.post(self.url, postData)
return result.text
def getPostData(self):
"""取得送出欄位資料
Returns:
{dict}: 欄位資料
"""
postData = {
'store_uid': self.storeUid,
'service': self.encrypt(self.getService(), self.storeKey),
'encry_data': self.encrypt(self.getRawData(), self.storeKey)
}
return postData
def run(self):
"""執行
"""
json = self.post(self.getPostData())
print(json)
StoreBatchDebitUpdater = StoreBatchDebitUpdater()
StoreBatchDebitUpdater.run()
回傳 JSON 結構如下:
{
"code": "200",
"msg": "資料正確",
"page_code": "16796e61b1",
"url": "http:\/\/pay.k20-mypay.tw\/regularinstallment\/link\/16796e61b1.html"
}
特約商店『請求修改定期定額網址』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name":"api","cmd":"api\/batchdebitupdater"} JSON格式,AES256加密資料 |
encry_data | text | 『請求修改定期定額網址』欄位參考 JSON格式,AES256加密資料 |
『請求修改定期定額網址』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | 必填 |
order_id | string | 請求編號(請求編號最長為50bytes),不可重複 | 必填 |
original_order_id | string | 原始單號,必須是當初發動建立定期定額的order_id | 必填 |
notify_before | string | 扣款前通知 | 『扣款通知』值參考 |
notify_before_message | string | 通知內容 | |
notify_after | string | 扣款後通知 | 『扣款通知』值參考 |
notify_after_message | string | 通知內容 | |
retry_enable | int | 扣款失敗後,是否啟用重扣機制(預設1) | 非必填 『扣款失敗後,重扣是否啟用』值參考 |
fail_notify_delay | string | 扣款失敗,延遲幾日後重扣,單位:日 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 僅能輸入3~5 |
retry_time | string | 扣款失敗後,要重試幾次重扣 | 專案名稱若已存在,則此蘭位於新增時無效,非必填 僅能輸入1~2 |
pay_mode_uid | integer | 付款方式 | 非必填 『卡片類型定義』值參考 |
card_lock | integer | 信用卡資料是否解鎖(預設0) | 非必填 『更新頁面設定鎖』值參考 |
member_lock | integer | 會員資料鎖(預設0) | 非必填 『更新頁面設定鎖』值參考 |
name | string | 扣款人姓名 | 非必填 |
debtor_type | integer | 扣款人身份類型(預設) | 非必填 『證號類型』值參考 |
debtor_id | string | 扣款人證號 | 非必填 |
phone_code | string | 扣款人手機國碼 | 非必填 |
phone | string | 扣款人手機 | |
string | 扣款人電子信箱 | ||
store_member_id | string | 網站會員ID | |
currency | string | 幣別(目前強制為TWD) | |
cost | string | 每期應扣金額 | 非必填 |
invoice_lock | int | 發票/收據 鎖(預設0) | 非必填 『更新頁面設定鎖』值參考 |
invoice_type | integer | 發票類型(預設0) | 非必填 『單據類型』值參考 |
cloud_type | int | 雲端載具類型(預設0) | 非必填 『雲端發票類型』值參考 |
invoice_mobile_code | string | 當cloud_type為2時紀錄的手機條碼 | |
invoice_natural_person | string | 當cloud_type為3時紀錄的自然人憑證條碼 | |
invoice_love_code | string | 當input_type為2時紀錄的愛心碼 | |
tong_bian | string | 發票統編 | 非必填 |
donation_statistics_type | int | 捐款收據統計模式(預設0),當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款收據統計模式』值參考 |
donation_identity_type | int | 捐款收據收件對象當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款單位』值參考 |
donation_type_no | string | 捐款收據對象身分證或統編,當donation_identity_type為1時填身分證2填統編,當donation_identity_type為1時必填 | |
donation_type_name | string | 捐款收據對象姓名或公司名稱,當donation_identity_type為1時填姓名2填公司名稱,當donation_identity_type為1時必填 | |
invoice_receipt_zip_code | string | 捐款收據地址郵政區碼 | |
invoice_receipt_city | string | 捐款收據地址縣市 | |
invoice_receipt_district | string | 捐款收據地址鄉區 | |
invoice_receipt_address | string | 捐款收據地址 | |
edit_debit_lock | int | 扣款設定是否解鎖(預設0) | 非必填 『更新頁面設定鎖』值參考 |
debit_setting_type | int | 扣款設定異動(預設0) | 非必填 『』值參考 |
stop_date | string | 停扣日,格式:YYYY-MM-DD | 非必填 |
delay_start_date | string | 延扣起始日 | |
delay_end_date | string | 延扣結束日 | |
stop_reason | string | 停扣、延扣原因 | |
uniform_invoice_tax_name | string | 免用統一發票買受人 | |
uniform_invoice_tax_id | string | 免用統一發票買受人統編 | |
echo_lock | int | 自訂資料鎖(預設0) | 非必填 『更新頁面設定鎖』值參考 |
echo_0 | string | 自訂回傳1 | 非必填 |
echo_1 | string | 自訂回傳2 | 非必填 |
echo_2 | string | 自訂回傳3 | 非必填 |
echo_3 | string | 自訂回傳4 | 非必填 |
echo_4 | string | 自訂回傳5 | 非必填 |
『請求修改定期定額網址』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回應代碼 | 『執行結果代碼』值參考 |
msg | string | 回應說明 | |
page_code | string | 頁面代碼 | |
url | string | 頁面網址 |
其他關聯欄位說明
關聯欄位
『分期類型定義』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
W | string | 每週定期扣款 | |
F | string | 雙週定期扣款 | |
M | string | 每月定期扣款 | |
S | string | 每季定期扣款 | |
H | string | 每半年定期扣款 | |
A | string | 每一年定期扣款 | |
O | string | 一次性扣款 |
『扣款通知』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 不通知 | ||
1 | |||
2 | 簡訊 |
『扣款失敗後,重扣是否啟用』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 否 | ||
1 | 是 |
『卡片類型定義』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | 信用卡 | ||
9 | 海外信用卡 | ||
29 | 美國運通 |
『證號類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 身份證字號(預設) | |
2 | integer | 統一證號 | |
3 | integer | 護照號碼 |
『單據類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 不開 | ||
1 | 雲端發票 | ||
2 | 捐贈 | ||
4 | 免用統一發票收據 | ||
5 | 捐贈收據 |
『雲端發票類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 未使用雲端發票類型 | |
2 | integer | 手機條碼 | |
3 | integer | 自然人憑證條碼 | |
4 | integer | 以E-Mail寄送 |
『捐款收據統計模式』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 無作用 | ||
1 | 單筆 | ||
2 | 年度 |
『捐款單位』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 無作用,預設 | ||
1 | 個人 | ||
2 | 公司 |
『更新頁面設定鎖』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | 不解除 | ||
1 | 解除 |
附錄一:定期定額請求建立、修改網址狀態碼
狀態代碼 | 狀態說明 | 詳細說明 |
---|---|---|
100 | 系統收到資料不正確。 | |
200 | 系統收到正確資料。 | |
400 | 系統錯誤 |
附錄二:定期定額,新增或修改背景通知結果回報欄位
當新增或修改結束後,若有在後台設定回傳通知,系統將會將此次修改結果回傳於設定的url
背景通知 JSON 結構如下:
{
"code": "A0000",
"msg": "成功",
"page_code": "4f10a91077",
"order_id": "T1709533895",
"indice_data": {
"name": "gogoro24期零利率",
"regular": "M",
"notify_before": "2",
"notify_before_message": "(s:-特店名稱-:)將於(s:-扣款月日-:)為(s:-產品名稱-:)扣款,信用卡帳單顯示:(s:-金流受款名稱-:),謝謝",
"notify_after": "0",
"notify_after_message": "(s:-特店名稱-:)將於(s:-扣款月日-:)為(s:-產品名稱-:)扣款,信用卡帳單顯示:(s:-金流受款名稱-:),謝謝",
"retry_enable": "1",
"fail_notify_delay": "3",
"retry_time": "1"
},
"member_data": {
"group_id": "A1709533895",
"pay_mode_uid": "1",
"name": "王安石",
"debtor_type": "1",
"debtor_id": "A123456789",
"phone_code": "886",
"phone": "900000000",
"mail": "[email protected]",
"store_member_id": "0900000000",
"currency": "TWD",
"cost": "100.0000",
"regular_first_charge_date": "2024-03-04",
"regular_stop_charge_date": "",
"invoice_type": "1",
"cloud_type": "4",
"invoice_mobile_code": "\/MHX377Q",
"invoice_natural_person": "",
"invoice_love_code": "",
"tong_bian": "",
"donation_statistics_type": "0",
"donation_identity_type": "0",
"donation_type_no": "",
"donation_type_name": "",
"invoice_receipt_zip_code": "",
"invoice_receipt_city": "",
"invoice_receipt_district": "",
"invoice_receipt_address": "",
"uniform_invoice_tax_name": "",
"uniform_invoice_tax_id": "",
"echo_0": "1",
"echo_1": "2",
"echo_2": "3",
"echo_3": "4",
"echo_4": "5"
}
}
『回傳值』
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回應代碼 | 『代碼表』值參考 |
msg | string | 代碼訊息 | |
page_code | string | 頁面代碼 | |
order_id | string | 商家請求單號 | |
indice_data | object | 扣款名目資料 | 『扣款名目資料』欄位參考 |
member_data | object | 消費者扣款資料 | 『消費者扣款資料』欄位參考 |
delay_date | array | 延期設定 | 每筆『延扣紀錄』欄位參考 |
『代碼表』
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A0000 | 新增/修改成功 | ||
A0001 | 請求網址過期 |
『扣款名目資料』
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
name | string | 專案名稱 | |
regular | string | 扣款單位 | 『分期類型定義』值參考 |
notify_before | string | 扣款前通知方式 | 『扣款通知』值參考 |
notify_before_message | string | 扣款前通知內容 | |
notify_after | string | 扣款後通知方式 | 『扣款通知』值參考 |
notify_after_message | string | 扣款後通知內容 | |
retry_enable | int | 扣款失敗後,是否啟用重扣機制 | 『扣款失敗後,重扣是否啟用』值參考 |
fail_notify_delay | string | 扣款失敗,延遲幾日後重扣,單位:日 | |
retry_time | string | 扣款失敗後,要重試幾次重扣 |
『消費者扣款資料』
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
group_id | string | 扣款單號(不可重複,每當交易回報時,會帶入此單號,使之識別扣款為同一筆申請的定期定額) | 必填 |
pay_mode_uid | integer | 付款方式 | 非必填 『卡片類型定義』值參考 |
name | string | 扣款人姓名 | 非必填 |
debtor_type | integer | 扣款人身份類型 | 非必填 『證號類型』值參考 |
debtor_id | string | 扣款人證號 | 非必填 |
phone_code | string | 扣款人手機國碼 | 非必填 |
phone | string | 扣款人手機 | |
string | 扣款人電子信箱 | ||
store_member_id | string | 網站會員ID | |
currency | string | 幣別(目前強制為TWD) | |
cost | string | 每期應扣金額 | 非必填 |
regular_first_charge_date | string | 起扣日期 | 非必填 |
regular_stop_charge_date | string | 扣款到期日 | 非必填 |
invoice_type | integer | 發票類型(預設0) | 非必填 『單據類型』值參考 |
cloud_type | int | 雲端載具類型(預設0) | 非必填 『雲端發票類型』值參考 |
invoice_mobile_code | string | 當cloud_type為2時紀錄的手機條碼 | |
invoice_natural_person | string | 當cloud_type為3時紀錄的自然人憑證條碼 | |
invoice_love_code | string | 當input_type為2時紀錄的愛心碼 | |
tong_bian | string | 發票統編 | 非必填 |
donation_statistics_type | int | 捐款收據統計模式(預設0),當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款收據統計模式』值參考 |
donation_identity_type | int | 捐款收據收件對象當invoice_type為5時必填,且不能為0 | 依狀況必填 『捐款單位』值參考 |
donation_type_no | string | 捐款收據對象身分證或統編,當donation_identity_type為1時填身分證2填統編,當donation_identity_type為1時必填 | |
donation_type_name | string | 捐款收據對象姓名或公司名稱,當donation_identity_type為1時填姓名2填公司名稱,當donation_identity_type為1時必填 | |
invoice_receipt_zip_code | string | 捐款收據地址郵政區碼 | |
invoice_receipt_city | string | 捐款收據地址縣市 | |
invoice_receipt_district | string | 捐款收據地址鄉區 | |
invoice_receipt_address | string | 捐款收據地址 | |
uniform_invoice_tax_name | string | 免用統一發票買受人 | |
uniform_invoice_tax_id | string | 免用統一發票買受人統編 | |
echo_0 | string | 自訂回傳1 | 非必填 |
echo_1 | string | 自訂回傳2 | 非必填 |
echo_2 | string | 自訂回傳3 | 非必填 |
echo_3 | string | 自訂回傳4 | 非必填 |
echo_4 | string | 自訂回傳5 | 非必填 |
『延扣紀錄』
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
delay_start_date | string | 延扣起始日 | |
delay_end_date | string | 延扣結束日 | |
delay_reason | string | 延扣原因 |
附錄三:回傳通知設定網址位置
系統設定->連線通知設定,通知網址的部分,僅限80跟443 port
附錄四:資料加密方式說明
- 所有的API送出HTTPs請求之欄位中,service 和 encry_data 欄位皆進行 AES256+BASE64 加密處理。
- AES加密,格式為CBC,長度為256bits,金鑰長度32,IV長度16,傳遞內文為加密後組合IV並經過Base64轉換後傳出。
- IV資料建議隨機產生。
PHP加密示意:
AesEncrypt -> base64_ecode($IV . $JSON)
C#加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Java加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Node.js加密示意:
AesEncrypt -> concat([IV,JSON], [IV_SIZE,JSON_SIZE]) -> toString('base64')
Python加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> base64.b64encode
附錄五:API模擬串接服務
提供模擬使用HTTP Protocol,透過POST方式傳遞資料到MYPAY, 並且得到回傳結果。
附錄六:測試區測試用信用卡卡號
下面卡號僅限在測試區測試使用
聯合信用卡中心閘道(其中MasterCard/JCB可測分期與紅利)
Visa | MasterCard | JCB | |
---|---|---|---|
卡號 | 4938170130000003 | 5430450100001219 | 3560500100001218 |
有效日期 | 1228 | 1218 | 1218 |
安全碼 | 985 | 214 | 023 |
3D交易密碼 | nccc1234 |
高鉅模擬信用卡(成功卡)
Visa | MasterCard | JCB | |
---|---|---|---|
一般交易 | 4761120010000492 | 5204247750001471 | 3566703300032801 |
有效日期 | 1122 | 1122 | 0922 |
安全碼 | 533 | 111 | 033 |
高鉅模擬信用卡(失敗卡)
MasterCard | |
---|---|
一般交易 | 5204247750001471 |
有效日期 | 1122 |
安全碼 | 111 |
高鉅模擬信用卡(3D)
說明 | 卡號 |
---|---|
3D交易 | 4003618704777729 |
有效日期 | 任意 |
安全碼 | 任意 |