網路串接刷卡機設計概要
目前此功能只支援商米P2 PRO、商米V2與商米V2s。
設備使用條件︰
- V2與V2s,請先確認機器上有Google Play服務。
- 於P2 PRO和V2下載"MYPAY",而商米V2s下載"MYPAY TAP"。
- 申請MYPAY商務代號。
- 由MYPAY將商務代號與設備上的Device ID綁定,取得裝置名稱。
- V2s僅能申請環匯亞太收單。
安全性設計
所有的付費要求發動都僅能從特約商店的銷售系統發出請求,將傳輸的資料以AES256加密,再透過HTTPS加密傳輸。所有付費資訊經過MYPAY Link匝道進行呼叫刷卡機,由刷卡機進行交易處理。
資料驗證
交易參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。
系統架構
所有請求與查詢只能從特約商店的銷售系統發動,由特約商店的伺服器利用伺服端服務程式發動交易,以避免交易資料被消費者竄改或攔截。為確保交易安全,MYPAY LINK 僅能接受 https connection,並注意僅接受TLS1.2以上協定。
MYPAY 目前提供之服務與格式
MYPAY 提供二種資料傳遞與查詢方式,應用情境如下:
(1)交易請求:特店之銷售系統向系統發動,開啟刷卡機交易之申請請求。
(2)交易查詢:特店之銷售系統向系統發動,若未接收到交易結果回報,可透過此方法確認交易狀況。
(3)重印發票或補印發票:當列印因無紙而未完成發票列印時或需要補印發票時,可以發動此功能重印發票或補印發票。
(4)補印簽單:當列印無紙或墨水不清時,可以發動此功能補印簽單。
(5)交易退款:金流交易退款,支援即時退款且未超過退款期限內之支付方式皆可使用此方式發動退款。
我們提供介接方式是透過https連線,只接受POST方式傳送交易資料。
介接網址
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/init |
正式區 | https://ka.mypay.tw/api/init |
資料加密方式
AES 256編碼 + base64編碼(附錄四資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
交易請求
<?php
/**
* 特約商店串接-網路串接刷卡機-信用卡類交易
*/
final class StoreCreditcard
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['items'] = [['id' => '1',
'name' => '商品名稱',
'cost' => '10',
'amount' => '1',
'total' => '10']];
$rawData['cost'] = 10;
$rawData['user_id'] = "phper";
$rawData['order_id'] = "1234567890";
$rawData['ip'] = "127.0.0.1"; // 此為消費者IP,會做為驗證用
$rawData['pfn'] = "CREDITCARD";
$rawData['device_name'] = "A01";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/postransaction'
);
}
/**
* 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;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreCreditcard = new StoreCreditcard();
$StoreCreditcard->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 StoreCreditcard {
/// <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() {
StoreCreditcard simulator = new StoreCreditcard();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private DataRequest GetRawData() {
DataRequest rawData = new DataRequest();
rawData.store_uid = this.storeUid;
ArrayList items = new ArrayList();
ProductDataRequest item = new ProductDataRequest();
item.id = "1";
item.name = "商品名稱";
item.cost = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "CREDITCARD";
rawData.device_name = "A01";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/postransaction";
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 DataRequest {
public string store_uid { get; set; }
public ArrayList items { get; set; }
public string cost { get; set; }
public string user_id { get; set; }
public string order_id { get; set; }
public string ip { get; set; }
public string pfn { get; set; }
public string device_name { get; set; }
}
/// <summary>
/// 串接服務請求欄位
/// </summary>
public class ServiceRequest {
public string service_name { get; set; }
public string cmd { get; set; }
}
/// <summary>
/// 商品資料欄位
/// </summary>
public class ProductDataRequest {
public string id { get; set; }
public string name { get; set; }
public string cost { get; set; }
public string amount { get; set; }
public string total { 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 StoreCreditcard {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCreditcard simulator = new StoreCreditcard();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
ArrayList items = new ArrayList();
Map<Object, Object> item = new HashMap<Object, Object>();
item.put("id", "0886449");
item.put("name", "商品名稱");
item.put("cost", "10");
item.put("amount", "1");
item.put("total", "10");
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("items", items);
rawData.put("cost", "10");
rawData.put("user_id", "phper");
rawData.put("order_id", "1234567890");
rawData.put("ip", "127.0.0.1"); // 此為消費者IP,會做為驗證用
rawData.put("pfn", "CREDITCARD");
rawData.put("device_name", "A01");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/postransaction");
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) {
e.printStackTrace();
}
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 StoreCreditcard() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCreditcard.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
user_id: "userid",
cost: "10",
order_id: "2020020210003",
ip: "127.0.0.1",
items: [{
'id': "0886449",
'name': "拿鐵",
'cost': "10",
'amount': "1",
'total': "10"
}],
pfn: "CREDITCARD",
device_name: "A01"
};
};
/**
* 取得服務位置
*/
StoreCreditcard.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/postransaction"
};
};
/**
* AES 256 加密
*/
StoreCreditcard.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 到主機
*/
StoreCreditcard.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCreditcard.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCreditcard.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCreditcard = new StoreCreditcard();
StoreCreditcard.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-信用卡類交易
"""
class StoreCreditcard:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'items': [
{
'id': '1',
'name': '商品名稱',
'cost': '10',
'amount': '1',
'total': '10'
}
],
'cost': 10,
'user_id': "phper",
'order_id': "1234567890",
'ip': "127.0.0.1",
'pfn': "CREDITCARD",
'device_name': "A01",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/postransaction'
}
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)
StoreCreditcard = StoreCreditcard()
StoreCreditcard.run()
交易回傳 JSON 結構如下:
{
"code": "B500",
"msg": "設備「A01」目前未連線,請確認"
}
銷售系統對MYPAY LINK發動刷卡機交易請求,透過刷卡機進行交易,如刷卡或掃碼後,完成交易動作,如有電子發票則刷卡機再進行列印動作。
- 整體流程圖 :
特約商店銷售系統發動交易參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/postransaction"} JSON格式,AES256加密資料 |
encry_data | text | 『銷售系統發動交易』欄位參考,JSON格式,AES256加密資料 |
『銷售系統發動交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | 特約商店代碼 | string | 必填 |
device_name | 設備編號 | string | 必填 |
cost | 訂單總金額 | string | 必填 |
currency | 預設交易幣別(預設為TWD新台幣) | string | |
order_id | 訂單編號 | string | 必填 |
items | 訂單內物品數 | array | 每筆『商品項目』欄位參考 |
echo_0 | 自訂回傳參數 1 | string | |
echo_1 | 自訂回傳參數 2 | string | |
echo_2 | 自訂回傳參數 3 | string | |
echo_3 | 自訂回傳參數 4 | string | |
echo_4 | 自訂回傳參數 5 | string | |
pfn | 支付方式支援的方式有 CREDITCARD(信用卡) OFFLINE(線下交易) LINEPAYON(LINEPay線上) PION(Pi錢包線上) WECHAT(微信線上) ALIPAY(支付寶線上) JKOON(街口支付線上) EASYWALLETON(悠遊付線上) PXPAYON(全支付線上) PLUSPAYON(全盈支付線上) CASH(自行收款) |
string | 必填 |
discount | 折價金額 (預設0) | string | |
shipping_fee | 運費(用以計算未來系統商合作時的統計費用) | string | |
user_id | 消費者帳號 | string | |
user_name | 消費者姓名 | string | |
user_real_name | 消費者真實姓名 | string | |
user_address | 消費者帳單地址 | string | |
user_sn_type | 1:身分證,2:統一證號,3:護照號碼 付款人為本國人為1,外國人2 or 3 | string | |
user_sn | 付款人身分證/統一證號/護照號碼 | string | |
user_phone | 消費者家用電話 | string | |
user_cellphone_code | 消費者行動電話國碼 | string | |
user_cellphone | 消費者行動電話 | string | |
user_email | 消費者 E-Mail | string | |
user_birthday | 消費者生日 | string | |
ip | 消費者來源 IP | string | |
issue_invoice_state | 電子發票是否開立0.不開立 1.開立 2.依系統設定(預設) | integer | |
invoice_print_type | 發票列印類型(預設只印電子發票) | 『電子發票列印類型』值參考integer | |
invoice_ratetype | 電子發票稅率別1: 應稅 2:零稅率 3: 免稅 | integer | |
invoice_input_type | 1.雲端發票 2.發票捐贈 3.實體發票 | string | |
invoice_cloud_type | 雲端發票內容2.手機條碼 3.自然人憑證條碼 4.以E-Mail寄送,當invoice_input_type為1,此狀態才有效 | string | |
invoice_tax_id | 統一編號,當invoice_input_type為1,此欄位才有效,非必要 | string | |
invoice_mobile_code | 手機條碼,當invoice_cloud_type為2,此欄位才有效 | string | |
invoice_natural_person | 自然人憑證條碼,當invoice_cloud_type為3,此欄位才有效 | string | |
invoice_m_post_zone | EMail 紙本寄送郵遞區號,當invoice_cloud_type為4,此欄位才有效,非必須 | string | |
invoice_m_address | EMail 紙本寄送住址,當invoice_cloud_type為4,此欄位才有效,非必須 | string | |
invoice_love_code | 愛心碼,當invoice_input_type為2,此欄位才有效 | string | |
invoice_b2b_title | 發票抬頭,當invoice_input_type為3時,此欄位才有效 | string | |
invoice_b2b_id | 統一編號,當invoice_input_type為3時,此欄位才有效 | string | |
invoice_b2b_post_zone | 發票郵遞區號,當invoice_input_type為3時,此欄位才有效,非必須 | string | |
invoice_b2b_address | 發票地址,當invoice_input_type為3時,此欄位才有效 | string | |
actual_pay_mode | string | 實際支付方式 (當pfn為CASH-自行收款時,告知系統實際收款方式) |
『自行收款付款方式』值參考 |
商品資訊
欄位 | 型態 | 說明 | 必填 |
---|---|---|---|
id | string(20) | 商品編號 | 必要 |
name | string(20) | 商品名稱 | 必要 |
cost | string(10) | 商品單價 | 必要 |
amount | string(10) | 商品數量 | 必要 |
total | string(20) | 商品小計 | 必要 |
回傳資料
欄位 | 說明 |
---|---|
code | 通常為B200,B500,參考交易回傳碼 |
msg | 訊息說明 |
『交易完成回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 『超商條碼繳費』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『非即時交易回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 『超商條碼繳費』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『訂單確認回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 『超商條碼繳費』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
訂單查詢
發動交易後,如果遲遲未接收回報,可透過此方法查詢訂單交易。
<?php
/**
* 特約商店串接-直接交易查詢
*/
final class StoreQueryDirect
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['order_id'] = '53654';
$rawData['user_id'] = 'phper';
$rawData['cost'] = '10';
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/querydirect'
);
}
/**
* 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;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreQueryDirect = new StoreQueryDirect();
$StoreQueryDirect->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 StoreQueryDirect {
/// <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() {
StoreQueryDirect simulator = new StoreQueryDirect();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private DataRequest GetRawData() {
DataRequest rawData = new DataRequest();
rawData.store_uid = this.storeUid;
rawData.order_id = "2020020210003";
rawData.user_id = "phper";
rawData.cost = "10";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/querydirect";
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 DataRequest {
public string store_uid { get; set; }
public string cost { get; set; }
public string user_id { get; set; }
public string order_id { get; set; }
}
/// <summary>
/// 串接服務請求欄位
/// </summary>
public class ServiceRequest {
public string service_name { get; set; }
public string cmd { get; set; }
}
/// <summary>
/// 商品資料欄位
/// </summary>
public class ProductDataRequest {
public string id { get; set; }
public string name { get; set; }
public string cost { get; set; }
public string amount { get; set; }
public string total { 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 StoreQueryDirect {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreQueryDirect simulator = new StoreQueryDirect();
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>();
rawData.put("store_uid", this.storeUid);
rawData.put("order_id", "2020020210003");
rawData.put("user_id", "phper");
rawData.put("cost", "10");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/querydirect");
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) {
e.printStackTrace();
}
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 StoreQueryDirect() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreQueryDirect.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
order_id: "2020020210003",
user_id: "phper",
cost: "10"
};
};
/**
* 取得服務位置
*/
StoreQueryDirect.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/querydirect"
};
};
/**
* AES 256 加密
*/
StoreQueryDirect.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 到主機
*/
StoreQueryDirect.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();
});
};
/**
* 取得送出欄位資料
*/
StoreQueryDirect.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreQueryDirect.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreQueryDirect = new StoreQueryDirect();
StoreQueryDirect.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-直接交易查詢
"""
class StoreQueryDirect:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'order_id': "2020020210003",
'user_id': "phper",
'cost': "10"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/querydirect'
}
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)
StoreQueryDirect = StoreQueryDirect()
StoreQueryDirect.run()
交易回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"rows": [
{
"uid": "",
"key": "",
"prc": "",
"finishtime": "",
"cardno": "",
"acode": "",
"order_id": "53654",
"user_id": "phper",
"cost": "10",
"currency": "",
"actual_cost": "",
"actual_currency": "",
"price": null,
"actual_price": null,
"recharge_code": null,
"love_cost": "0",
"retmsg": "查無資料",
"pfn": "",
"trans_type": "1",
"payment_name": "",
"nois": "",
"group_id": "",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
]
}
特約商店交易查詢參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/querydirect"} JSON格式,AES256加密資料 |
encry_data | text | 『交易查詢』欄位參考,JSON格式,AES256加密資料 |
『訂單查詢』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | 特約商店編號 |
string | 必填 |
order_id | 訂單編號 |
string | 必填 |
cost | 訂單總金額 |
string | 必填 |
user_id | 消費者帳號 |
string |
回傳資料
欄位 | 說明 | 型態 | 補充說明 |
---|---|---|---|
code | 執行結果 | string | 參考交易回傳碼 |
msg | 執行結果訊息 | string | |
rows | array | 查詢資料清單 | 交易成功可能含多筆退款或取消以及發票等相關資訊 每筆『交易查詢』欄位參考 |
『交易查詢』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | string | 付款種類 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 虛擬帳號銀行代碼 | |
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
appropriation_date | string | 預計撥款日期(YYYYMMDD) | |
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 『超商條碼繳費』值參考 |
refund_order | array | 退款訂單資訊(多筆格式) | 每筆『交易查詢-退款資訊』欄位參考 |
cancel_order | array | 取消訂單資訊(多筆格式) | 每筆『交易查詢-取消資訊』欄位參考 |
invoice_state | integer | 發票開立狀態 | 『電子發票開立狀態類型』值參考 |
invoice_date | string | 發票開立日期(YYYYMMDD) | |
invoice_wordtrack | string | 發票字軌 | |
invoice_number | string | 發票號碼 | |
invoice_rand_code | string | 電子發票隨機碼 | |
invoice_seller_ban | string | 賣方統一編號 | |
invoice_buyer_ban | string | 買方統一編號 | |
invoice_left_qrcode | string | 電子發票左邊QrCode內容 | |
invoice_middle_barcode | string | 電子發票中間Barcode內容(格式Code-39) | |
invoice_right_qrcode | string | 電子發票右邊QrCode內容 | |
invoice_title_type | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_title | string | 電子發票列印標題格式 | |
invoice_print_type | integer | 電子發票列印類型 | 『電子發票列印類型』值參考 |
invoice_print_device | integer | 電子發票列印設備 | 『電子發票列印設備』值參考 |
invoice_amount | string | 電子發票銷售總額 | |
invoice_sales_amount | string | 電子發票銷售額 | |
invoice_tax_amount | string | 電子發票稅額 | |
invoice_order_detail | string | 電子發票全部產品明細(JSON格式) | 『商品細項』值參考 |
invoice_ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string | 電子發票開立類型-雲端發票類型 | 『雲端發票類型』值參考 |
invoice_mobile_code | string | 當invoice_cloud_type為2時紀錄的手機條碼 | |
invoice_tax_id | string | 當invoice_cloud_type為2時紀錄的統一編號 | |
invoice_natural_person | string | 當invoice_cloud_type為3時紀錄的自然人憑證條碼 | |
invoice_m_post_zone | string | 當invoice_cloud_type為4時紀錄中獎時紙本發票郵遞區號 | |
invoice_m_address | string | 當invoice_cloud_type為4時紀錄中獎時紙本發票收件住址 | |
invoice_love_code | string | 當invoice_input_type為2時紀錄的愛心碼 | |
invoice_b2b_title | string | 當invoice_input_type為3時紀錄的發票抬頭 | |
invoice_b2b_id | string | 當invoice_input_type為3時紀錄的統一編號 | |
invoice_b2b_post_zone | string | 當invoice_input_type為3時紀錄的郵遞區號 | |
invoice_b2b_address | string | 當invoice_input_type為3時紀錄的發票地址 | |
invoice_allowance | array | 電子發票折讓資訊 | 每筆『電子發票折讓資訊』欄位參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
重印發票或補印發票
當列印因無紙而未完成發票列印時或需要補印發票時,可以發動此功能重印發票或補印發票
<?php
/**
* 特約商店串接-重印發票或補印發票
*/
final class StoreReprintInvoice
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_name'] = "A01";
$rawData['uid'] = "70233";
$rawData['key'] = "755f6c2afda6c252d5ade6e50da32ab8";
$rawData['type'] = 1;
$rawData['print_type'] = 2;
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/posreprintinvoice'
);
}
/**
* 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;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreReprintInvoice = new StoreReprintInvoice();
$StoreReprintInvoice->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 StoreReprintInvoice {
/// <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() {
StoreReprintInvoice simulator = new StoreReprintInvoice();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.device_name = "A01";
rawData.uid = "70233";
rawData.key = "755f6c2afda6c252d5ade6e50da32ab8";
rawData.type = 1;
rawData.print_type = 2;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/posreprintinvoice";
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 StoreReprintInvoice {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreReprintInvoice simulator = new StoreReprintInvoice();
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>();
rawData.put("store_uid", this.storeUid);
rawData.put("device_name", "A01");
rawData.put("uid", "70233");
rawData.put("key", "755f6c2afda6c252d5ade6e50da32ab8");
rawData.put("type", 1);
rawData.put("print_type", 2);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/posreprintinvoice");
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) {
e.printStackTrace();
}
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 StoreReprintInvoice() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreReprintInvoice.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_name: "A01",
uid: "70233",
key: "755f6c2afda6c252d5ade6e50da32ab8",
type: 1,
print_type: 2,
};
};
/**
* 取得服務位置
*/
StoreReprintInvoice.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/posreprintinvoice"
};
};
/**
* AES 256 加密
*/
StoreReprintInvoice.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 到主機
*/
StoreReprintInvoice.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();
});
};
/**
* 取得送出欄位資料
*/
StoreReprintInvoice.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreReprintInvoice.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreReprintInvoice = new StoreReprintInvoice();
StoreReprintInvoice.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-重印發票或補印發票
"""
class StoreReprintInvoice:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_name': "A01",
'uid': "70233",
'key': "755f6c2afda6c252d5ade6e50da32ab8",
'type': 1,
'print_type': 2,
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/posreprintinvoice'
}
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)
StoreReprintInvoice = StoreReprintInvoice()
StoreReprintInvoice.run()
交易回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功。"
}
特約商店重印發票或補印發票參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/posreprintinvoice"} JSON格式,AES256加密資料 |
encry_data | text | 『重印發票或補印發票』欄位參考,JSON格式,AES256加密資料 |
『重印發票或補印發票』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店編號 | 必填 |
device_name | string | 設備編號 | 必填 |
uid | string | 訂單UID | |
key | string | 查詢驗證碼 | |
type | integer | 發票列印種類 | 『發票列印種類』值參考 |
print_type | integer | 電子發票列印類型(預設只印電子發票) | 『電子發票列印類型』值參考 |
『重印發票或補印發票』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼參考交易回傳碼 | |
msg | string | 回傳訊息 |
補印簽單
<?php
/**
* 特約商店串接-補印簽單
*/
final class StoreReprintSignature
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_name'] = "A01";
$rawData['uid'] = "70233";
$rawData['key'] = "755f6c2afda6c252d5ade6e50da32ab8";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/posreprintsignature'
);
}
/**
* 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;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreReprintSignature = new StoreReprintSignature();
$StoreReprintSignature->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 StoreReprintSignature {
/// <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() {
StoreReprintSignature simulator = new StoreReprintSignature();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.device_name = "A01";
rawData.uid = "70233";
rawData.key = "755f6c2afda6c252d5ade6e50da32ab8";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/posreprintsignature";
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 StoreReprintSignature {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreReprintSignature simulator = new StoreReprintSignature();
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>();
rawData.put("store_uid", this.storeUid);
rawData.put("device_name", "A01");
rawData.put("uid", "70233");
rawData.put("key", "755f6c2afda6c252d5ade6e50da32ab8");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/posreprintsignature");
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) {
e.printStackTrace();
}
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 StoreReprintSignature() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreReprintSignature.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_name: "A01",
uid: "70233",
key: "755f6c2afda6c252d5ade6e50da32ab8",
};
};
/**
* 取得服務位置
*/
StoreReprintSignature.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/posreprintsignature"
};
};
/**
* AES 256 加密
*/
StoreReprintSignature.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 到主機
*/
StoreReprintSignature.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();
});
};
/**
* 取得送出欄位資料
*/
StoreReprintSignature.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreReprintSignature.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreReprintSignature = new StoreReprintSignature();
StoreReprintSignature.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-補印簽單
"""
class StoreReprintSignature:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_name': "A01",
'uid': "70233",
'key': "755f6c2afda6c252d5ade6e50da32ab8",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/posreprintsignature'
}
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)
StoreReprintSignature = StoreReprintSignature()
StoreReprintSignature.run()
交易回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功。"
}
當列印無紙或墨水不清時,可以發動此功能補印簽單。
特約商店補印簽參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/posreprintsignature"} JSON格式,AES256加密資料 |
encry_data | text | 『補印簽單』欄位參考,JSON格式,AES256加密資料 |
『補印簽單』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店商務代號 | 必填 |
device_name | string | 設備編號 | 必填 |
uid | string | 訂單UID | 必填 |
key | string | 查詢驗證碼 | 必填 |
receipt_type | string | 補印簽單列印類型 | 『銷售系統對設備發動簽單列印類型』值參考 |
『補印簽單』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | 參考附錄二 |
msg | string | 回傳訊息 |
交易退款
<?php
/**
* 特約商店串接-交易退款
*/
final class StoreRefund
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_name'] = "A01";
$rawData['uid'] = "88833";
$rawData['key'] = "1c943777706d68f757afdb9034213001";
$rawData['cost'] = 100;
$rawData['invoice_state'] = 6;
$rawData['items'] = [
[
'id' => '1',
'name' => '倚天劍模型',
'cost' => 100,
'amount' => 1,
'total' => 100
]
];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/posrefund'
);
}
/**
* 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;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
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;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreRefund = new StoreRefund();
$StoreRefund->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 StoreRefund {
/// <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() {
StoreRefund simulator = new StoreRefund();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
ArrayList items = new ArrayList();
dynamic item = new ExpandoObject();
item.id = "1";
item.name = "倚天劍模型";
item.cost = 100;
item.amount = 1;
item.total = 100;
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.device_name = "A01";
rawData.uid = "88833";
rawData.key = "1c943777706d68f757afdb9034213001";
rawData.cost = 100;
rawData.invoice_state = 6;
rawData.items = items;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/posrefund";
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 StoreRefund {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreRefund simulator = new StoreRefund();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
ArrayList items = new ArrayList();
Map<Object, Object> item = new HashMap<Object, Object>();
item.put("id", "1");
item.put("name", "倚天劍模型");
item.put("cost", 100);
item.put("amount", 1);
item.put("total", 100);
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("device_name", "A01");
rawData.put("uid", "88833");
rawData.put("key", "1c943777706d68f757afdb9034213001");
rawData.put("cost", 100);
rawData.put("invoice_state", 6);
rawData.put("items", items);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/posrefund");
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) {
e.printStackTrace();
}
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 StoreRefund() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreRefund.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_name: "A01",
uid: "88833",
key: "1c943777706d68f757afdb9034213001",
cost: 100,
invoice_state: 6,
items: [
{
'id': '1',
'name': '倚天劍模型',
'cost': 100,
'amount': 1,
'total': 100
}
],
};
};
/**
* 取得服務位置
*/
StoreRefund.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/posrefund"
};
};
/**
* AES 256 加密
*/
StoreRefund.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 到主機
*/
StoreRefund.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();
});
};
/**
* 取得送出欄位資料
*/
StoreRefund.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreRefund.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreRefund = new StoreRefund();
StoreRefund.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-交易退款
"""
class StoreRefund:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_name': "A01",
'uid': "88833",
'key': "1c943777706d68f757afdb9034213001",
'cost': 100,
'invoice_state': 6,
'items': [
{
'id': '1',
'name': '倚天劍模型',
'cost': 100,
'amount': 1,
'total': 100
}
],
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/posrefund'
}
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)
StoreRefund = StoreRefund()
StoreRefund.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功。"
}
若需要退款時,發動此API提出退款請求。支援即時退款且未超過退款期限內之支付方式皆可使用此方式發動退款。 支援即時退款的支付方式有信用卡、美國運通、LINEPay、Pi錢包、街口支付、微信支付、支付寶、悠遊付、自行收款、GooglePay、ApplePay。
特約商店『交易退款』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/posrefund"} JSON格式,AES256加密資料 |
encry_data | text | 『交易退款』欄位參考 JSON格式,AES256加密資料 |
『交易退款』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店商務代號 | 必填 |
key | string | 交易驗証碼 | |
uid | string | 訂單編號(UID) | |
cost | string | 退款金額 | 必填 |
invoice_state | string | 若有開立電子發票,指定電子發票使用作廢或折讓 注意:跨發票月份無法作廢 |
『電子發票退款時使用作廢或折讓』值參考 |
items | array | 退款項目 若使用電子發票,此欄位必填 退款項目之項目名稱必須和交易時之產品項目名稱相同 退款項目之總金額必須與退款金額一致 |
每筆『商品項目』欄位參考 |
device_name | string | 設備編號 | 必填 |
receipt_type | string | 簽單列印類型(信用卡專用參數) | 『銷售系統對設備發動簽單列印類型』值參考 |
『交易退款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | 參考附錄二 |
msg | string | 回傳訊息 |
其他關聯欄位說明
關聯欄位
『紅利資訊』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
type | string | 紅利類型 | 『紅利資訊類型』值參考 |
used | string | 紅利折抵點數 | |
amount | string | 自付金額 |
『虛擬帳號回傳欄位』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
PinCode | string | 虛擬帳號 | |
LimitDate | string | 繳費有效期限,格式YYYYMMDDHHmmss | |
BankCode | string | 銀行代碼 |
『ibon』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
PinCode | string | 超商代碼(可用qrcode被掃) | |
LimitDate | string | 超商代碼繳費有效期限,格式YYYYMMDDHHmmss | |
BarCode1 | string | 三段條碼繳費條碼1(格式:Code-39 barcode) | |
BarCode2 | string | 三段條碼繳費條碼2(格式:Code-39 barcode) | |
BarCode3 | string | 三段條碼繳費條碼3(格式:Code-39 barcode) | |
BarcodeEndDate | string | 三段條碼繳費期限,格式YYYYMMDDHHmmss |
『FamiPort』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
PinCode | string | 繳費代碼(可憑此代碼至設備列印繳費單) | |
LimitDate | string | 繳費有效期限,格式YYYYMMDDHHmmss | |
BarCode1 | string | 三段條碼繳費條碼1(格式:Code-39 barcode) | |
BarCode2 | string | 三段條碼繳費條碼2(格式:Code-39 barcode) | |
BarCode3 | string | 三段條碼繳費條碼3(格式:Code-39 barcode) | |
BarcodeEndDate | string | 三段條碼繳費期限,格式YYYYMMDDHHmmss |
『Life-ET』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
PinCode | string | 繳費代碼(可憑此代碼至設備列印繳費單) | |
BarCode1 | string | 繳費條碼1(格式:Code-39 barcode) | |
BarCode2 | string | 繳費條碼2(格式:Code-39 barcode) | |
BarCode3 | string | 繳費條碼3(格式:Code-39 barcode) | |
LimitDate | string | 繳費有效期限,格式YYYYMMDDHHmmss |
『超商條碼繳費』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
BarCode1 | string | 繳費條碼1(格式:Code-39 barcode) | |
BarCode2 | string | 繳費條碼2(格式:Code-39 barcode) | |
BarCode3 | string | 繳費條碼3(格式:Code-39 barcode) | |
LimitDate | string | 繳費有效期限,格式YYYYMMDDHHmmss |
『交易查詢-退款資訊』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
prc | string | 主要交易回傳碼(retcode) | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
retmsg | string | 回傳訊息 | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
appropriation_date | string | 預計撥款日期(YYYYMMDD) | |
invoice_state | integer | 發票開立狀態 | 『電子發票開立狀態類型』值參考 |
invoice_date | string | 發票開立日期(YYYYMMDD) | |
invoice_wordtrack | string | 發票字軌 | |
invoice_number | string | 發票號碼 | |
invoice_rand_code | string | 電子發票隨機碼 | |
invoice_seller_ban | string | 賣方統一編號 | |
invoice_buyer_ban | string | 買方統一編號 | |
invoice_left_qrcode | string | 電子發票左邊QrCode內容 | |
invoice_middle_barcode | string | 電子發票中間Barcode內容(格式Code-39) | |
invoice_right_qrcode | string | 電子發票右邊QrCode內容 | |
invoice_title_type | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_title | string | 電子發票列印標題內容 | |
invoice_print_type | integer | 電子發票列印類型 | 『電子發票列印類型』值參考 |
invoice_print_device | integer | 電子發票列印設備 | 『電子發票列印設備』值參考 |
invoice_amount | string | 電子發票銷售總額 | |
invoice_sales_amount | string | 電子發票銷售額 | |
invoice_tax_amount | string | 電子發票稅額 | |
invoice_order_detail | string | 電子發票全部產品明細(JSON格式) | 『商品細項』值參考 |
invoice_ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_allowance | array | 電子發票折讓資訊 | 每筆『電子發票折讓資訊』欄位參考 |
『交易查詢-取消資訊』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
prc | string | 主要交易回傳碼(retcode) | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
retmsg | string | 回傳訊息 | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) |
『電子發票折讓資訊』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | 發生之退款交易流水號(UID) | |
amount | integer | 電子發票折讓金額 | |
order_detail | string | 電子發票折讓明細(JSON格式) | 『商品細項』值參考 |
『退款完成回傳資訊』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | 原MYPAYLINK 之交易流水號 | |
refund_uid | string | 退款之交易流水號(若多次退款,每次皆會不同) | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 退款處理完成時間(YYYYMMDDHHmmss) | |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 申請之退款金額 | |
currency | string | 申請之退款幣別 | |
actual_cost | string | 實際退款金額 | |
actual_currency | string | 實際退款幣別 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
refund_type | string | 退款類型(1.直接線上退款 2.手動退款(信用卡類) 3.手動退款(現金類) | |
expected_refund_date | string | 現金退款預計退款日(YYYYMMDD) | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『商品項目』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
id | string | 商品編號 | 必填 |
name | string | 商品名稱 | 必填 |
cost | integer | 商品單價 | 必填 |
amount | integer | 商品數量 | 必填 |
total | integer | 商品小計 | 必填 |
值的定義
『信用卡別類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | string | 無法辨識或支付方式為非信用卡類 | |
1 | string | VISA | |
2 | string | MasterCard | |
3 | string | JCB | |
4 | string | AMEX |
『交易服務類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 尚未進行閘道交易 | |
1 | integer | 代收代付 | |
2 | integer | 特店模式 |
『金流供應商代碼』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A1 | string | 裕富數位資融 | |
A2 | string | 三環亞洲 | |
B0 | string | 新光銀行 | |
B1 | string | 永豐銀行 | |
B2 | string | 合作金庫 | |
B3 | string | 台北富邦 | |
B4 | string | 玉山銀行 | |
B5 | string | 台新銀行 | |
B6 | string | 聯合信用卡處理中心 | |
B7 | string | 台中商銀 | |
B8 | string | 中國信託商業銀行 | |
B9 | string | 上海商業儲蓄銀行 | |
BA | string | 第一銀行 | |
BB | string | 元大商業銀行 | |
BC | string | 凱基銀行 | |
BD | string | 國泰世華商業銀行 | |
S0 | string | 全網行銷股份有限公司(FamiPort) | |
S1 | string | 安源資訊股份有限公司(ibon) | |
S2 | string | 萊爾富國際股份有限公司(Hi-Life) | |
T0 | string | 高鉅科技 | |
T1 | string | 藍新金流 | |
W0 | string | 統振 | |
W1 | string | 遊戲橘子數位 | |
W2 | string | 台灣連線(LINEPay) | |
W3 | string | 博經 | |
W4 | string | 街口電子支付 | |
W5 | string | 悠遊卡 | |
W6 | string | 一卡通票證 | |
W7 | string | iCash | |
W8 | string | 全支付(PXPay plus) | |
W9 | string | 拍付國際資訊(Pi錢包) | |
E0 | string | MYTIX |
『交易類型定義』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 一般 (預設) | |
2 | integer | 分期 | |
3 | integer | 紅利 |
『閘道內容回傳格式類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 無法辨識 | |
1 | integer | 網址 | |
2 | integer | 超連結本文 | |
3 | integer | xml | |
4 | integer | json | |
5 | integer | csv | |
6 | integer | 串流 |
『資料內容所屬支付名稱』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
E_COLLECTION | string | 虛擬帳號 | |
IBON | string | iBON | |
FAMIPORT | string | FamiPort | |
LIFEET | string | LIFE-ET | |
WEBATM | string | WEBATM | |
CREDITCARD | string | 信用卡 | |
UNIONPAY | string | 銀聯卡 | |
SVC | string | 點數卡(GASH ,Imoney) | |
ABROAD | string | 海外信用卡 | |
ALIPAY | string | 支付寶 | |
string | 微信支付 | ||
LINEPAYON | string | LINE Pay線上付款 | |
LINEPAYOFF | string | LINE Pay線下付款 | |
WECHATOFF | string | 微信支付線下 | |
APPLEPAY | string | APPLE PAY | |
GOOGLEPAY | string | Google Pay | |
EACH | string | eACH交易 | |
CARDLESS | string | 無卡分期 | |
PION | string | Pi 拍錢包線上 | |
PIOFF | string | Pi 拍錢包線下 | |
AMEX | string | 美國運通 | |
JKOON | string | 街口支付線上 | |
JKOOFF | string | 街口支付線下 | |
ALIPAYOFF | string | 支付寶線下 | |
M_RECHARGE | string | 儲值交易 | |
EASYWALLETON | string | 悠遊付線上 | |
EASYWALLETOFF | string | 悠遊付線下 | |
AFP | string | 後付款 | |
CASH | string | 自行收款 | |
BARCODE | string | 超商條碼繳費 |
『電子發票開立狀態類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 不處理(預設) | |
1 | integer | 等候處理中, | |
2 | integer | 發票開立成功 | |
3 | integer | 發票處理失敗 | |
4 | integer | 作癈 | |
5 | integer | 系統或特約商店發票號碼設定不正確 | |
6 | integer | 折讓 |
『電子發票紙本列印標題類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 文字 | |
2 | integer | 圖形(圖片網址) |
『電子發票列印類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 不列印 自行處置 | |
1 | integer | 列印 電子發票 + 商品明細 | |
2 | integer | 只印電子發票 | |
3 | integer | 只印商品明細 |
『電子發票列印設備』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 自行處理 | |
1 | integer | SUNMI V2 PRO |
『商品細項』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
Description | string | 商品名稱 | 必填 |
Quantity | string | 數量 | 必填 |
UnitPrice | string | 單價 | 必填 |
Amount | string | 總金額 | 必填 |
『電子發票稅率別』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 應稅(預設) | |
2 | integer | 零稅率 | |
3 | integer | 免稅 |
『電子發票開立類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 雲端發票 | |
2 | integer | 發票捐贈 | |
3 | integer | 實體發票 |
『雲端發票類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
2 | integer | 手機條碼 | |
3 | integer | 自然人憑證條碼 | |
4 | integer | 以E-Mail寄送 |
『紅利資訊類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 全額 | |
2 | integer | 部分 |
『發票列印種類』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 重印發票(正式發票) | |
2 | integer | 補印發票 |
『電子發票退款時使用作廢或折讓』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
4 | string | 作廢或作廢重開 | 預設 |
6 | string | 折讓 |
『銷售系統對設備發動簽單列印類型』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | string | 列印全部(商店存根&持卡人存根)(預設) | |
2 | string | 只印持卡人存根 |
『自行收款付款方式』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
REMITTENCE | string | 轉帳匯款 | |
CASH | string | 現金交易 | |
ZINGALAPAY | string | 銀角零卡 | |
LINEPAY | string | LINE Pay | |
JKO | string | 街口支付 | |
PI | string | Pi 拍錢包 | |
EASYWALLET | string | 悠遊付 | |
PXPAY | string | 全支付 | |
PLUSPAY | string | 全盈支付 | |
ALIPAY_YUANTABANK | string | 支付寶元大 | |
ALIPAY_MEGABANK | string | 支付寶兆豐 | |
ALIPAY_TCB | string | 支付寶合庫 | |
WECHAT_SKBANK | string | 微信支付新光 | |
MOMO | string | Momo收款 | |
SHOPEE | string | 蝦皮拍賣收款 | |
ETMALL | string | 東森收款 | |
PCSTORE | string | PChome商店街收款 | |
PCHOME24H | string | PChome24H收款 | |
RUTEN | string | 露天拍賣收款 | |
SHOPLINE | string | Shop line收款 | |
FOODPANDA | string | 熊貓收款 | |
UBEREATS | string | Uber eats收款 | |
CYBERBIZ | string | Cyberbiz收款 | |
YAHOOBID | string | YAHOO拍賣收款 | |
RAKUTEN | string | 樂天市場收款 | |
CAROUSELL | string | 旋轉拍賣收款 | |
FACEBOOKGROUPS | string | FB社團收款 | |
PINKOI | string | Pinkoi收款 | |
91APP | string | 91APP收款 | |
COD_MYPAY | string | MYPAY物流代收 | |
COD_HCT | string | 新竹物流代收 | |
COD_TCAT | string | 黑貓宅急便代收 | |
COD_MYSHIP711 | string | 7-ELEVEN賣貨便代收 | |
COD_FAMISTORE | string | 好賣+代收 | |
COD_HISHIPBUYER | string | 萊賣貨代收 | |
COD_KERRYTJ | string | 嘉里大榮代收 | |
CSTORECODE_IBON | string | 超商代碼(IBON) | |
CSTORECODE_FAMIPORT | string | 超商代碼(FamiPort) | |
CSTORECODE_LIFEET | string | 超商代碼(Life-ET) | |
CSTORECODE_OKGO | string | 超商代碼(OK GO) | |
ETICKET_EASYCARD | string | 悠遊卡 | |
ETICKET_IPASS | string | 一卡通 | |
ETICKET_ICASH | string | iCash | |
CRYPTO_BTC | string | 比特幣 | |
CRYPTO_ETH | string | 乙太幣 | |
TELECOM_CHT | string | 中華電信代收 | |
TELECOM_FET | string | 遠傳電信代收 | |
TELECOM_TWM | string | 台灣大哥大代收 | |
TELECOM_TSTAR | string | 台灣之星代收 | |
TELECOM_APT | string | 亞太電信代收 | |
ECPAY_CREDIT | string | 綠界收款(信用卡) | |
ECPAY_WEBATM | string | 綠界收款(網路ATM) | |
ECPAY_ATM | string | 綠界收款(自動櫃員機) | |
ECPAY_CVS | string | 綠界收款(超商代碼) | |
ECPAY_BARCODE | string | 綠界收款(超商條碼) | |
ECPAY_TWQR | string | 綠界收款(行動支付) | |
ECPAY_IN_STORE_PICKUP | string | 綠界收款(超取) | |
NEWEBPAY_CREDIT | string | 藍新收款(信用卡付款) | |
NEWEBPAY_VACC | string | 藍新收款(銀行 ATM 轉帳付款) | |
NEWEBPAY_WEBATM | string | 藍新收款(網路銀行轉帳付款) | |
NEWEBPAY_BARCODE | string | 藍新收款(超商條碼繳費) | |
NEWEBPAY_CVS | string | 藍新收款(超商代碼繳費) | |
NEWEBPAY_LINEPAY | string | 藍新收款(LINE Pay 付款) | |
NEWEBPAY_ESUNWALLET | string | 藍新收款(玉山 Wallet) | |
NEWEBPAY_TAIWANPAY | string | 藍新收款(台灣 Pay) | |
NEWEBPAY_CVSCOM | string | 藍新收款(超商取貨付款) | |
CREDITCARD_TCB | string | 信用卡(合庫銀行) | |
CREDITCARD_ESUNBANK | string | 信用卡(玉山銀行) | |
CREDITCARD_FIRSTBANK | string | 信用卡(第一銀行) | |
CREDITCARD_FUBONBANK | string | 信用卡(台北富邦銀行) | |
CREDITCARD_TSIB | string | 信用卡(台新銀行) | |
CREDITCARD_NCCC | string | 信用卡(聯信) | |
CREDITCARD_CTBC | string | 信用卡(中信銀行) | |
CREDITCARD_KGIBANK | string | 信用卡(凱基銀行) | |
CREDITCARD_YUANTABANK | string | 信用卡(元大銀行) | |
CREDITCARD_CUB | string | 信用卡(國泰世華銀行) | |
CREDITCARD_BANKSINOPAC | string | 信用卡(永豐銀行) | |
CREDITCARD_MEGABANK | string | 信用卡(兆豐銀行) | |
CREDITCARD_GLOBALPAYMENTS | string | 信用卡(環匯亞太) | |
AMEX_TSIB | string | 美國運通(台新銀行) | |
AMEX_CTBC | string | 美國運通(中信銀行) | |
AMEX_NCCC | string | 美國運通(聯信) | |
UNIONPAY_TCB | string | 銀聯卡(合庫銀行) | |
UNIONPAY_ESUNBANK | string | 銀聯卡(玉山銀行) | |
UNIONPAY_NCCC | string | 銀聯卡(聯信) | |
UNIONPAY_TSIB | string | 銀聯卡(台新銀行) | |
UNIONPAY_KGIBANK | string | 銀聯卡(凱基銀行) | |
UNIONPAY_YUANTABANK | string | 銀聯卡(元大銀行) | |
UNIONPAY_BANKSINOPAC | string | 銀聯卡(永豐銀行) | |
UNIONPAY_CUB | string | 銀聯卡(國泰世華銀行) | |
BARCODE_SKBANK | string | 超商繳費條碼(新光銀行) | |
BARCODE_ESUNBANK | string | 超商繳費條碼(玉山銀行) | |
BARCODE_CTBC | string | 超商繳費條碼(中信銀行) | |
TWPAY_BOT | string | 台灣Pay(台灣銀行) | |
TWPAY_LANDBANK | string | 台灣Pay(台灣土地銀行) | |
TWPAY_TCB | string | 台灣Pay(合庫銀行) | |
TWPAY_FIRSTBANK | string | 台灣Pay(第一銀行) | |
TWPAY_HNCB | string | 台灣Pay(華南銀行) | |
TWPAY_CHB | string | 台灣Pay(彰化銀行) | |
TWPAY_SCSB | string | 台灣Pay(上海商銀) | |
TWPAY_CUB | string | 台灣Pay(國泰世華銀行) | |
TWPAY_MEGABANK | string | 台灣Pay(兆豐銀行) | |
TWPAY_BOK | string | 台灣Pay(高雄銀行) | |
TWPAY_TBB | string | 台灣Pay(台灣企銀) | |
TWPAY_KTB | string | 台灣Pay(京城銀行) | |
TWPAY_HWATAIBANK | string | 台灣Pay(華泰銀行) | |
TWPAY_SUNNYBANK | string | 台灣Pay(陽信銀行) | |
TWPAY_KSCC | string | 台灣Pay(基隆二信) | |
TWPAY_TFCCBANK | string | 台灣Pay(淡水一信) | |
TWPAY_HCFCBANK | string | 台灣Pay(新竹一信) | |
TWPAY_TSCA | string | 台灣Pay(台中二信) | |
TWPAY_CH6C | string | 台灣Pay(彰化六信) | |
TWPAY_HL2C | string | 台灣Pay(花蓮二信) | |
TWPAY_ESUNBANK | string | 台灣Pay(玉山銀行) | |
TWPAY_TSIB | string | 台灣Pay(台新銀行) | |
TWPAY_AFISC | string | 台灣Pay(農金資) | |
TWPAY_YUANTABANK | string | 台灣Pay(元大銀行) | |
TWPAY_CTBC | string | 台灣Pay(中信銀行) | |
TWPAY_FAST | string | 台灣Pay(南農中心) | |
TWPAY_SCU | string | 台灣Pay(南資中心) | |
DS_SHINSHIN | string | 欣欣大眾 | |
DS_MINGYAO | string | 明曜百貨 | |
DS_DAYEH | string | 大葉高島屋 | |
DS_CHUNGYO | string | 中友百貨 | |
DS_KSSOGO | string | 廣三SOGO百貨 | |
DS_FOCUSQUARE | string | Focus時尚流行館 | |
DS_NICEPLAZA | string | 耐斯廣場 | |
DS_DREAMMALL | string | 夢時代購物中心 | |
DS_UNIUSTYLE | string | 統一時代百貨 | |
DS_PARKLANES | string | 金典綠園道商場 | |
DS_CITYPLAZA | string | 大都會廣場 | |
DS_LIHPAOMALL | string | 麗寶百貨廣場 | |
DS_EDORAPARK | string | 瀚星百貨 | |
DS_BEYONDPLAZA | string | 比漾廣場 | |
DS_HAYASHI | string | 林百貨 | |
DS_SHINESQUARE | string | 昕境廣場 | |
DS_LANDMARKLIFEPLAZA | string | 置地生活廣場 | |
DS_TONLIN | string | 統領廣場 | |
DS_HONHUI | string | 宏匯廣場 | |
DS_TSRD | string | 南紡購物中心 | |
DS_FEDS_XINYIA13 | string | 遠東百貨-遠百信義 A13 | |
DS_FEDS_BANQIAO | string | 遠東百貨-板橋中山店 | |
DS_FEDS_TAOYUAN | string | 遠東百貨-桃園店 | |
DS_FEDS_ZHUPEI | string | 遠東百貨-竹北店 | |
DS_FEDS_CHIAYI | string | 遠東百貨-嘉義店 | |
DS_FEDS_HUALIEN | string | 遠東百貨-花蓮店 | |
DS_FEDS_MEGACITY_BANQIAO | string | 遠東百貨-MegaCity板橋大遠百 | |
DS_FEDS_HSINCHU | string | 遠東百貨-新竹大遠百 | |
DS_FEDS_TOPCITY_TAICHUNG | string | 遠東百貨-Top City台中大遠百 | |
DS_FEDS_TAINAN_GONGYUAN | string | 遠東百貨-台南大遠百公園店 | |
DS_FEDS_TAINAN_CHENGKUNG | string | 遠東百貨-台南大遠百成功店 | |
DS_FEDS_KAOHSIUNG | string | 遠東百貨-高雄大遠百 | |
DS_SKM_XINYI | string | 新光三越-台北信義新天地 | |
DS_SKM_TAIPEI_STATION | string | 新光三越-台北站前店 | |
DS_SKM_TAIPEI_NANJING_W | string | 新光三越-台北南西店 | |
DS_SKM_TAIPEI_TIANMU | string | 新光三越-台北天母店 | |
DS_SKM_TAOYUAN_STATION | string | 新光三越-桃園站前店 | |
DS_SKM_TCAIHUNG_PORT | string | 新光三越-台中中港店 | |
DS_SKM_CHIAYI_CHUIYANG | string | 新光三越-嘉義垂楊店 | |
DS_SKM_TAINAN_ZHONGSHAN | string | 新光三越-台南中山店 | |
DS_SKM_TAINAN_XIMEN | string | 新光三越-台南西門店 | |
DS_SKM_KAOHSIUNG_SANDUO | string | 新光三越-高雄三多店 | |
DS_SKM_KAOHSIUNG_ZUOYING | string | 新光三越-高雄左營店 | |
DS_SKM_PARK | string | 新光三越-SKM Park Outlets 高雄草衙 | |
DS_SOGO_TAIPEI_ZHONGXIAO | string | 遠東SOGO百貨-台北忠孝館 | |
DS_SOGO_TAIPEI_FUXING | string | 遠東SOGO百貨-台北復興館 | |
DS_SOGO_TAIPEI_DUNHUA | string | 遠東SOGO百貨-台北敦化館 | |
DS_SOGO_TIANMU | string | 遠東SOGO百貨-天母店 | |
DS_SOGO_ZHONGLI | string | 遠東SOGO百貨-中壢店 | |
DS_SOGO_HSINCHU | string | 遠東SOGO百貨-新竹店 | |
DS_SOGO_KAOHSIUNG | string | 遠東SOGO百貨-高雄店 | |
DS_PACIFIC_FENGYUAN | string | 太平洋百貨-豐原 | |
DS_PACIFIC_PINGTUNG | string | 太平洋百貨-屏東 | |
DS_PACIFIC_SUNNYPARK | string | 太平洋百貨-Sunny Park日光廣場 | |
DS_PACIFIC_PINGTUNG_GONGYONG | string | 太平洋百貨-屏東驛站商場 | |
DS_PACIFIC_PINGTUNG_GUANGFU | string | 太平洋百貨-屏東轉運站商場 | |
DS_PACIFIC_CHAOZHOU | string | 太平洋百貨-潮州驛站商場 | |
DS_HANSHIN_KAOHSIUNG | string | 漢神百貨 | |
DS_HANSHIN_ARENA | string | 漢神巨蛋購物廣場 | |
DS_BREEZE_CENTER | string | 微風廣場 | |
DS_BREEZE_TAIPEISTATION | string | 微風台北車站 | |
DS_BREEZE_NTUHOSPITAL | string | 微風台大醫院商場 | |
DS_BREEZE_NANJING | string | 微風南京 | |
DS_BREEZE_XINYI | string | 微風信義 | |
DS_BREEZE_SONGGAO | string | 微風松高 | |
DS_BREEZE_NANSHAN | string | 微風南山 | |
DS_BREEZE_TSGHOSPITAL | string | 微風三總商店街 | |
DS_BREEZE_ACADEMIASINICA | string | 微風中央研究院 | |
DS_GM_ZHONGHE | string | GlobalMall-新北中和 | |
DS_GM_BANQIAO | string | GlobalMall-環球板橋車站 | |
DS_GM_A8 | string | GlobalMall-環球桃園A8 | |
DS_GM_A19 | string | GlobalMall-環球桃園A19 | |
DS_GM_A9 | string | GlobalMall-環球林口A9 | |
DS_GM_NANGANG | string | GlobalMall-環球南港車站 | |
DS_GM_XINZUOYING | string | GlobalMall-環球新左營車站 | |
DS_GM_PINGTUNG | string | GlobalMall-環球屏東市 | |
DS_TRK_MALL | string | 大魯閣新時代購物中心 | |
DS_TRK_SQUARE | string | 大魯閣湳雅廣場 | |
DS_TALEE_WUFU | string | 大立大統五福店 | |
DS_TALEE_A | string | 大立百貨A館 | |
DS_TALEE_B | string | 大立百貨B館 | |
DS_MITSUI_OUTLET_TAICHUNGPORT | string | MITSUI OUTLET PARK 台中港 | |
DS_MITSUI_OUTLET_TAINAN | string | MITSUI OUTLET PARK 台南 | |
DS_MITSUI_OUTLET_LINKOU | string | MITSUI OUTLET PARK 林口 | |
DS_MITSUI_LALAPORT_TAICHUNG | string | Mitsui Shopping Park LaLaport 台中 |
附錄一:PFN(支付工具)參數表
資料傳遞可使用編號,也可以使用代碼 。 例如:pfn=27跟pfn=PION,一樣都是使用Pi 拍錢包線上付款支付工具。
編號 | 代碼 | 狀態 | 說明 |
---|---|---|---|
10 | ALIPAY | 啟用 | 支付寶 |
13 | 啟用 | 微信支付 | |
15 | LINEPAYON | 啟用 | LINE線上付款(消費者主掃) |
27 | PION | 啟用 | Pi 拍錢包線上付款(消費者主掃) |
31 | JKOON | 啟用 | 街口支付線上付款(消費者主掃) |
37 | CASH | 啟用 | 現金支付 |
38 | EASYWALLETON | 啟用 | 悠遊付線上付款 |
附錄二:交易狀態代碼
以下回傳的狀態代碼均須處理,避免系統連線錯誤,MYPAY LINK回傳時,貴司系統無法判讀
狀態代碼 | 狀態說明 | 詳細說明 |
---|---|---|
100 | 資料錯誤 | MYPAYLINK收到資料,但是格式或資料錯誤 |
200 | 資料正確 | MYPAYLINK收到正確資料,會接續下一步交易 |
220 | 取消成功 | 如申請取消,取消訂單狀態為取消成功 |
230 | 退款成功 | 如申請退款,申請退款成功時狀態。 |
250 | 付款成功 | 此次交易,消費者付款成功 |
280 | 交易成功 尚未付款完成 |
儲值/WEBATM-線上待付款,等待狀態,等到使用者線上完成交易後MYPAY LINK會再傳送一次結果 250:代表消費者付款成功,此為最終結果 300:代表消費者付款失敗 |
290 | 交易成功 但資訊不符 |
交易成功,但資訊不符(包含金額不符,如多繳或少繳...等),該類型交易請特別注意 |
300 | 交易失敗 | 金流服務商回傳交易失敗或該筆交易超過風險控管限制規則 |
400 | 系統錯誤訊息 | 若MYPAY LINK或上游服務商系統異常時 |
600 | 結帳完成 | 視為付款完成,此狀態為上游服務商確認訂單後的狀態,表示該筆訂單會撥款 透過MYPAY主動查詢或每日對帳機制 操作訂單功能內發動查詢功能 |
A0001 | 交易待確認 | MYPAY LINK與金流服務商發生連線異常,待查詢後確認結果,會主動再次回傳交易結果 250:代表消費者確實付款完成 600:結帳完成 300:金流服務商回傳交易失敗或超過風險控管限制規則交易 |
A0002 | 放棄交易 | 畫面導向MYPAY LINK後,消費者即放棄該筆交易,該筆交易視同交易失敗,為最終結果 |
B200 | 執行成功 | 處理成功執行 |
B500 | 執行失敗 | 處理時,資料異常不予以處理 |
附錄三:設定調整
- 交易回傳設定
- 交易金鑰重新發送與變更
附錄四:資料加密方式說明
- 所有的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, 並且得到回傳結果。