特約商店 MYPAY LINK 直接交易
修改歷程
版本 | 異動日期 | 修訂內容 | 位置 |
---|---|---|---|
1.0 | 2021.01.01 | 新版文件 | |
1.0.1 | 2023.04.07 | 新增開發前請先閱讀 | 20230407_01 |
1.0.2 | 2023.07.26 | 新增特約商店可用支付工具 | 20230726_01 |
開發前請先閱讀
金鑰的注意事項
Q:金鑰的有效期限
A:MyPay的金鑰有效期限只有一年,如果要延長,請自行至金鑰管理系統延長
Q:金鑰可以在什麼時候延長
A:到期日7天前,都可以延長(不包含第7天),一但超過,系統自動產生新金鑰後,即不可再延長
Q:金鑰即將到期前7天時,系統新發的金鑰是否會於信件中夾帶
A:會
Q:金鑰在到期前何時會發信通知
A:定期發送金鑰到期通知的頻率為:60天/30天/20天,若 貴司皆無動作,則系統會於到期前七天產製新金鑰並發送email到 貴司技術窗口信箱,此時舊的金鑰,因還未過期還可以使用,一旦過期,貴司仍使用舊金鑰則會無法交易
交易結果的告知
Q:何時會給予交易結果
A:當mypay接收到交易的http request後,會立即回應交易結果於http response body,除此之外,MyPay也會由附錄三這邊的設定,背景主動發動通知商家
Q:當我接到MyPay通知時該做什麼
A:請直接回應8888,如沒有回應,MyPay會通知5次,若5次內均得不到8888,系統會寄發信件給予商家的技術人員以及附錄三而外設定的人員
行動支付測試
Q:那些行動支付工具可以做測試
A:目前除了line pay外,其餘行動支付,因上游無提供測試資料,皆無法進行測試
串接錯誤
Q:為什麼我出現403錯誤
A:我司使用AWS WAF 防火牆,有針對user-agent做限制,請檢查user-agent是否帶入正常的資料,何謂正常的user-agent,可以參考Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
直接交易設計概要
安全性設計
所有的付費要求發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES加密,再透過HTTPS加密傳輸。交易資料不由消費者端送出,可確保資料不被消費者竄改。所有付費資訊經過MYPAY Link匝道進行轉送處理,特約商店不需要處理消費者的付費流程以及安全控管。
資料驗證
交易參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。 大部分交易模式,特約商店以導頁的方式處理付費流程,特約商店只需確保與MYPAY LINK連線正常,即可確保交易安全以及確保資料安全。
系統架構
所有的交易請求與查詢只能透過伺服器發動,由特約商店的網頁伺服器利用伺服端服務程式發動交易,以避免交易資料被消費者竄改或攔截。為確保交易安全,MYPAY Link僅能接受 https connection,注意僅接受TLS1.2以上協定。
MYPAY LINK 目前提供之服務與格式
MYPAY LINK 提供多種交易與應用方式,應用情境如下:
(1)虛擬帳號交易請求:產生一組虛擬帳號,可透過ATM轉帳。
(2)超商代碼交易請求:產生ibon、FamiPort、Hi-Life等繳費代碼或繳費條碼自行產生條碼後供超商掃碼繳費。
(3)WebATM交易請求:產生指定金額之導頁網頁資料到銀行WebATM做交易。
(30)後付款交易請求:提交消費者與出貨資訊給後付款。
(31)後付款請款請求:提交貨運資訊給後付款確認請款。
(32)後付款更正出貨請求:未請款前,更正出貨的資訊。
(33)後付款查詢出貨請求:查詢後付款出貨相關資訊。
(34)電票交易請求:發動電子票證交易,消費者透過感應卡機進行付款消費。
(35)電票查詢卡號請求:查詢電子票證之卡號資訊。
(36)電票查詢餘額請求:查詢電子票證餘額。
(37)電票退款請求:發動電子票證交易之退款,消費者透過感應卡機退回電子票證。
(38)eACH交易請求:當消費者申請eDDA電子化授權核印通過後,可發動此交易請求做交易動作。
(39)超商條碼繳費:消費至超商或繳費單位使用條碼繳費。
(40)全支付線上交易請求:商家串接MyPay,直接取得付款網址或開啟APP。
(41)全支付線下交易請求:消費者提供PXPay plus支付碼供設備進行掃碼做交易動作。
(42)LINE Pay線上交易請求:商家串接MyPay,直接取得付款網址或開啟APP。
(43)LINE Pay線下交易請求:消費者提供LINE Pay支付碼供設備進行掃碼做交易動作。
(44)Pi線上交易請求:商家串接MyPay,直接取得付款網址或開啟APP。
(45)Pi線下交易請求:消費者提供Pi支付碼供設備進行掃碼做交易動作。
(46)微信線下交易請求:消費者提供微信支付碼供設備進行掃碼做交易動作。
(47)街口線下交易請求:商家串接MyPay,直接取得付款網址或開啟APP。
(48)街口線下交易請求:消費者提供街口支付碼供設備進行掃碼做交易動作。
(49)Apple Pay線下交易請求:消費者提供Apple Pay支付碼供設備進行掃碼做交易動作。
(50)Google Pay線下交易請求:消費者提供Google Pay支付碼供設備進行掃碼做交易動作。
(51)悠遊付線下交易請求:消費者提供悠遊付支付碼供設備進行掃碼做交易動作。
(52)支付寶線下交易請求:消費者提供支付寶支付碼供設備進行掃碼做交易動作。
(53)自行收款交易請求:商店自行向消費者收款時,可發動此交易請求,將收款記錄在MYPAY
(54)直接交易查詢:向系統發動,若未接收到交易結果回報,可透過此方法確認交易狀況。
(55)交易退款:金流交易退款,支援即時退款且未超過退款期限內之支付方式皆可使用此方式發動退款。
(56)全盈支付線上交易請求:商家串接MyPay,直接取得付款網址或開啟APP。
(57)全盈支付線下交易請求:消費者提供全盈支付碼供設備進行掃碼做交易動作。
我們提供介接方式是透過https連線,只接受POST方式傳送交易資料。
介接網址
特約商店模式
位置 | API介接網址 |
---|---|
測試區 | https://ka.usecase.cc/api/init |
正式區 | https://ka.mypay.tw/api/init |
Client模式(限定功能)
位置 | API介接網址 |
---|---|
測試區 | https://ka.usecase.cc/api/open |
正式區 | https://ka.mypay.tw/api/open |
資料加密方式
AES 256編碼 + base64編碼(附錄四資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
虛擬帳號交易
<?php
/**
* 特約商店串接-直接交易-虛擬帳號
*/
final class StoreECollection
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.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'] = "E_COLLECTION";
$rawData['limit_pay_days'] = 7;
$rawData['supplier_code'] = "B2";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/transaction'
);
}
/**
* 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;
}
}
$StoreECollection = new StoreECollection();
$StoreECollection->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 StoreECollection {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreECollection simulator = new StoreECollection();
//僅限走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 = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "E_COLLECTION";
rawData.limit_pay_days = "7";
rawData.supplier_code = "B2";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/transaction";
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 StoreECollection {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreECollection simulator = new StoreECollection();
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", "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", "E_COLLECTION");
rawData.put("limit_pay_days", "7");
rawData.put("supplier_code", "B2");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/transaction");
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 StoreECollection() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreECollection.prototype.getRawData = function () {
return {
store_uid: this.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: "E_COLLECTION",
limit_pay_days: 7,
supplier_code: "B2"
};
};
/**
* 取得服務位置
*/
StoreECollection.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
StoreECollection.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 到主機
*/
StoreECollection.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();
});
};
/**
* 取得送出欄位資料
*/
StoreECollection.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreECollection.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreECollection = new StoreECollection();
StoreECollection.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 StoreCStoreCode:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.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': "CSTORECODE",
'limit_pay_days': "3",
'supplier_code': "S0"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/transaction'
}
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)
StoreCStoreCode = StoreCStoreCode()
StoreCStoreCode.run()
回傳 JSON 結構如下:
{
"code": "270",
"msg": "執行成功。",
"uid": 75568,
"key": "3bb205d8f2c315d073fc62c5de8d9761",
"finishtime": "",
"cardno": "",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 10,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"pfn": "E_COLLECTION",
"trans_type": 1,
"result_type": 4,
"result_content": "{\n \"PinCode\": \"1255100349000018\",\n \"LimitDate\": \"20201214113117\",\n \"BankCode\": \"006\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
發動交易後,得到一組銀行代號與繳費帳號,讓消費者透過ATM或臨櫃作繳費動作。
特約商店『虛擬帳號交易』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data | text | 『虛擬帳號交易』欄位參考 JSON格式,AES256加密資料 |
『虛擬帳號交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string(16) | 特約商店代碼 | 必填 |
cost | string(19) | 訂單總金額 | 必填 |
currency | string(10) | 預設交易幣別(預設為TWD新台幣) | |
order_id | string(50) | 訂單編號 | 必填 |
items | array | 訂單內物品數 | 每筆『商品項目』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 | |
pfn | string(10) | 付費方法 | 必填 『付款方式』值參考 |
discount | string(19) | 折價金額 (預設0) | |
shipping_fee | string(19) | 運費 | |
user_id | string(50) | 消費者帳號 | |
user_name | string(20) | 消費者姓名 | |
user_real_name | string(20) | 消費者真實姓名 | |
user_address | string(50) | 消費者帳單地址 | |
user_sn_type | string(1) | 證號類型 | 『證號類型』值參考 |
user_sn | string(19) | 付款人身分證/統一證號/護照號碼 | |
user_phone | string(20) | 消費者家用電話 | |
user_cellphone_code | string(6) | 消費者行動電話國碼 | |
user_cellphone | string(20) | 消費者行動電話 | |
user_email | string(80) | 消費者 E-Mail | |
user_birthday | string(10) | 消費者生日 | |
ip | string(50) | 消費者來源 IP | |
issue_invoice_state | integer(1) | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer(1) | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer(1) | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string(1) | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string(8) | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string(20) | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string(20) | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string(20) | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string(50) | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string(8) | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone | string(10) | 發票郵遞區號 | 當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address | string(200) | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
agent_sms_fee_type | integer(1) | 經銷商代收費是否含簡訊費 (0.不含 1.含) | 『含不含簡訊費』值參考 |
agent_charge_fee_type | integer(1) | 經銷商代收費是否含手續費 (0.不含 1.含) | 『含不含手續費類型』值參考 |
agent_charge_fee | string(9) | 經銷商代收費 | |
is_agent_charge | integer(1) | 是否為經銷商代收費模式 若 is_agent_charge 有指定,以指定優先 若欄位agent_charge_fee有費用,或經銷商代收費是含簡訊費、或經銷商代收費含手續費,則預設為1 若欄位agent_charge_fee無費用,且經銷商代收費不含簡訊費、或經銷商代收費不含手續費或參欄位都未使用則預設為0 |
『是否為經銷商代收費模式』值參考 |
supplier_code | string | 金融供應商代碼(依合約設定之支援金融供應商) | 必填 『金流供應商代碼』值參考 |
limit_pay_days | string | 繳費有效天數(如無此資料以系統設定為預設) |
『虛擬帳號』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | 『虛擬帳號回傳欄位』值參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
超商代碼交易
<?php
/**
* 特約商店串接-直接交易-超商代碼
*/
final class StoreCStoreCode
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.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'] = "CSTORECODE";
$rawData['limit_pay_days'] = 3;
$rawData['supplier_code'] = "S0";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/transaction'
);
}
/**
* 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;
}
}
$StoreCStoreCode = new StoreCStoreCode();
$StoreCStoreCode->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 StoreCStoreCode {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreCStoreCode simulator = new StoreCStoreCode();
//僅限走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 = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "CSTORECODE";
rawData.limit_pay_days = "3";
rawData.supplier_code = "S0";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/transaction";
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 StoreCStoreCode {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCStoreCode simulator = new StoreCStoreCode();
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", "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", "CSTORECODE");
rawData.put("limit_pay_days", "3");
rawData.put("supplier_code", "S0");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/transaction");
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 StoreCStoreCode() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCStoreCode.prototype.getRawData = function () {
return {
store_uid: this.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: "CSTORECODE",
limit_pay_days: "3",
supplier_code: "S0"
};
};
/**
* 取得服務位置
*/
StoreCStoreCode.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
StoreCStoreCode.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 到主機
*/
StoreCStoreCode.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCStoreCode.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCStoreCode.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCStoreCode = new StoreCStoreCode();
StoreCStoreCode.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 StoreECollection:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.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': "E_COLLECTION",
'limit_pay_days': 7,
'supplier_code': "B2"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/transaction'
}
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)
StoreECollection = StoreECollection()
StoreECollection.run()
回傳 JSON 結構如下:
{
"code": "260",
"msg": "執行成功。",
"uid": 75569,
"key": "0696b3e97553b8b2d4bdd845e2663aec",
"finishtime": "",
"cardno": "",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 10,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"pfn": "CSTORECODE",
"trans_type": 1,
"result_type": 4,
"result_content": "{\n \"PinCode\": \"201231V0000156\",\n \"BarCode1\": \"091207KK1\",\n \"BarCode2\": \"00201231V0000156\",\n \"BarCode3\": \"114332000000010\",\n \"LimitDate\": \"20201210113252\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
發動交易後,會依超商不同而得到不同的繳費代碼或繳費條碼。依規格產生相關的Qrcode或Barcode,提供消費者至超商作掃碼繳費動作。
特約商店『超商代碼交易』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data | text | 『超商代碼交易』欄位參考 JSON格式,AES256加密資料 |
『超商代碼交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string(16) | 特約商店代碼 | 必填 |
cost | string(20) | 訂單總金額 | 必填 |
currency | string(10) | 預設交易幣別(預設為TWD新台幣) | |
order_id | string(50) | 訂單編號 | 必填 |
items | array | 訂單內物品數 | 每筆『商品項目』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 | |
pfn | string | 付費方法 | 必填 『付款方式』值參考 |
discount | string | 折價金額 (預設0) | |
shipping_fee | string | 運費 | |
user_id | string | 消費者帳號 | |
user_name | string | 消費者姓名 | |
user_real_name | string | 消費者真實姓名 (如supplier_code = T2,此欄位有填,黑貓Pay發出E-Mail會有此資訊) |
|
user_zipcode | string | 消費者郵遞區號 (如supplier_code = T2,此欄位有填,黑貓Pay發出E-Mail會有此資訊) |
|
user_address | string | 消費者地址 (如supplier_code = T2,此欄位有填,黑貓Pay發出E-Mail會有此資訊) |
|
user_sn_type | string | 證號類型 | 『證號類型』值參考 |
user_sn | string | 付款人身分證/統一證號/護照號碼 | |
user_phone | string | 消費者家用電話 | |
user_cellphone_code | string | 消費者行動電話國碼 | |
user_cellphone | string | 消費者行動電話 (如supplier_code = T2,此欄位有填,會由黑貓Pay發出簡訊通知。註:測試環境黑貓Pay也可能會發送簡訊,勿隨意填) |
|
user_email | string | 消費者 E-Mail (如supplier_code = T2,此欄位有填,則會由黑貓Pay發出E-Mail通知到這裡) |
|
user_birthday | string | 消費者生日 | |
ip | string | 消費者來源 IP | |
issue_invoice_state | integer | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
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時,此欄位才有效 |
agent_sms_fee_type | integer | 經銷商代收費是否含簡訊費 (0.不含 1.含) | 『含不含簡訊費』值參考 |
agent_charge_fee_type | integer | 經銷商代收費是否含手續費 (0.不含 1.含) | 『含不含手續費類型』值參考 |
agent_charge_fee | string | 經銷商代收費 | |
is_agent_charge | integer | 是否為經銷商代收費模式 若 is_agent_charge 有指定,以指定優先 若欄位agent_charge_fee有費用,或經銷商代收費是含簡訊費、或經銷商代收費含手續費,則預設為1 若欄位agent_charge_fee無費用,且經銷商代收費不含簡訊費、或經銷商代收費不含手續費或參欄位都未使用則預設為0 |
『是否為經銷商代收費模式』值參考 |
supplier_code | string | 金融供應商代碼(依合約設定之支援金融供應商) | 必填 『金流供應商代碼』值參考 |
limit_pay_days | string | 繳費有效天數(如無此資料以系統設定為預設),FamiPort有限定3日內 |
『超商代碼交易』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | 『ibon』值參考 『FamiPort』值參考 『Hi Life』值參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
WebATM交易
<?php
/**
* 特約商店串接-直接交易-WebATM
*/
final class StoreWebATM
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.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'] = "WEBATM";
$rawData['supplier_code'] = "B2";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/transaction'
);
}
/**
* 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;
}
}
$StoreWebATM = new StoreWebATM();
$StoreWebATM->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>
/// 特約商店串接-直接交易-WebATM
/// </summary>
public class StoreWebATM {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreWebATM simulator = new StoreWebATM();
//僅限走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 = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "WEBATM";
rawData.supplier_code = "B2";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/transaction";
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;
/**
* 特約商店串接-直接交易-WebATM
* 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 StoreWebATM {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreWebATM simulator = new StoreWebATM();
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", "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", "WEBATM");
rawData.put("supplier_code", "B2");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/transaction");
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');
/**
* 特約商店串接-直接交易-WebATM
*/
function StoreWebATM() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreWebATM.prototype.getRawData = function () {
return {
store_uid: this.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: "WEBATM",
supplier_code: "B2"
};
};
/**
* 取得服務位置
*/
StoreWebATM.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
StoreWebATM.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 到主機
*/
StoreWebATM.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();
});
};
/**
* 取得送出欄位資料
*/
StoreWebATM.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreWebATM.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreWebATM = new StoreWebATM();
StoreWebATM.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-直接交易-WebATM
"""
class StoreWebATM:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.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': "WEBATM",
'supplier_code': "B2"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/transaction'
}
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)
StoreWebATM = StoreWebATM()
StoreWebATM.run()
回傳 JSON 結構如下:
{
"code": "280",
"msg": "執行成功。",
"uid": 75570,
"key": "e79ddd457c540d12b8b09cd7790cf614",
"finishtime": "",
"cardno": "",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 10,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"pfn": "WEBATM",
"trans_type": 1,
"result_type": 2,
"result_content": "<!DOCTYPE html>\r\n<html>\r\n<form action='http:\/\/eatms.tcb-bank.com.tw\/fShop' method='POST' id='form1' name='form1' >\r\n<input type='hidden' name='CardBillRq' value='PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iYmlnNSI\/Pg0KPENhcmRCaWxsUnEgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSI+DQogIDxTZW5kU2VxTm8+MjAyMDEyMDcxMDAwMDAwNzU1NzA8L1NlbmRTZXFObz4NCiAgPFR4VHlwZT4yNTYwPC9UeFR5cGU+DQogIDxQQVlOTz4wMDYyODkxNTE4ODUzMDE8L1BBWU5PPg0KICA8UGF5VHlwZT41OTk5OTwvUGF5VHlwZT4NCiAgPFVzZXJEYXRhPj8\/Pz88L1VzZXJEYXRhPg0KICA8T05PPjc1NTcwPC9PTk8+DQogIDxBY2N0SWRUbz4xMjU1MTAwMzQ1MDAwMDE2PC9BY2N0SWRUbz4NCiAgPEN1ckFtdD4xMDwvQ3VyQW10Pg0KICA8UGF5RHQ+MjAyMDEyMTA8L1BheUR0Pg0KICA8TUFDPmw0MlVaL3EwUkg1M0l5a21kczJGb1lGRG01eXJ4a3pCPC9NQUM+DQogIDxSc1VSTD5odHRwczovL3Rndy5teXBheS50dy9hcGkvd2ViYXRtL0V4dGVybmFsVGNiQ2xvc2VDYXNlPC9Sc1VSTD4NCjwvQ2FyZEJpbGxScT4='><!-- 帳號-->\r\n<input id='sub' type='submit' value='submit' style='display:none' \/>\r\n<script>\r\n document.getElementById('sub').click();\r\n<\/script>\r\n<\/form>\r\n<\/html>\r\n",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
當發動交易後,會得到一個轉址網頁資訊,透過網頁導頁方式,將消費者操作頁面導頁到銀行WebATM做交易。
特約商店『WebATM交易』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data | text | 『WebATM交易』欄位參考 JSON格式,AES256加密資料 |
『WEBATM交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string(16) | 特約商店代碼 | 必填 |
cost | string(19) | 訂單總金額 | 必填 |
currency | string(10) | 預設交易幣別(預設為TWD新台幣) | |
order_id | string(50) | 訂單編號 | 必填 |
items | array | 訂單內物品數 | 每筆『商品項目』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 | |
pfn | string(10) | 付費方法 | 必填 『付款方式』值參考 |
discount | string(19) | 折價金額 (預設0) | |
shipping_fee | string(19) | 運費 | |
user_id | string(50) | 消費者帳號 | |
user_name | string(20) | 消費者姓名 | |
user_real_name | string(20) | 消費者真實姓名 | |
user_address | string(50) | 消費者帳單地址 | |
user_sn_type | string(1) | 證號類型 | 『證號類型』值參考 |
user_sn | string(19) | 付款人身分證/統一證號/護照號碼 | |
user_phone | string(20) | 消費者家用電話 | |
user_cellphone_code | string(6) | 消費者行動電話國碼 | |
user_cellphone | string(20) | 消費者行動電話 | |
user_email | string(80) | 消費者 E-Mail | |
user_birthday | string(10) | 消費者生日 | |
ip | string(50) | 消費者來源 IP | |
issue_invoice_state | integer(1) | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer(1) | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer(1) | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string(1) | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string(8) | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string(20) | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string(20) | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string(20) | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string(50) | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string(8) | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone | string(10) | 發票郵遞區號 | 當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address | string(200) | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
agent_sms_fee_type | integer(1) | 經銷商代收費是否含簡訊費 (0.不含 1.含) | 『含不含簡訊費』值參考 |
agent_charge_fee_type | integer(1) | 經銷商代收費是否含手續費 (0.不含 1.含) | 『含不含手續費類型』值參考 |
agent_charge_fee | string(9) | 經銷商代收費 | |
is_agent_charge | integer(1) | 是否為經銷商代收費模式 若 is_agent_charge 有指定,以指定優先 若欄位agent_charge_fee有費用,或經銷商代收費是含簡訊費、或經銷商代收費含手續費,則預設為1 若欄位agent_charge_fee無費用,且經銷商代收費不含簡訊費、或經銷商代收費不含手續費或參欄位都未使用則預設為0 |
『是否為經銷商代收費模式』值參考 |
supplier_code | string | 金融供應商代碼(依合約設定之支援金融供應商) | 必填 『金流供應商代碼』值參考 |
success_returl | string(255) | 交易成功導頁網址 | |
failure_returl | string(255) | 交易失敗導頁網址 |
『WEBATM交易』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
後付款交易
<?php
/**
* 特約商店串接-直接交易-後付款
*/
final class StoreAfp
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['items'] = [['id' => '1',
'name' => '商品名稱',
'cost' => '1000',
'amount' => '1',
'total' => '1000']];
$rawData['cost'] = 1000;
$rawData['user_id'] = "phper";
$rawData['user_real_name'] = "金城武";
$rawData['user_zipcode'] = "407";
$rawData['user_address'] = "台中市西屯區市政路402號5樓之1";
$rawData['user_cellphone'] = "0900000123";
$rawData['user_email'] = "[email protected]";
$rawData['order_id'] = "1234567890";
$rawData['ip'] = "127.0.0.1"; // 此為消費者IP,會做為驗證用
$rawData['pfn'] = "AFP";
$rawData['shipping_info'] = ['shipment_type' => 2,
'name' => "金城武",
'tel' => '0900000123',
'zip_code' => '432',
'ship_address' => '台中市大肚區磺溪里15號'];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/transaction'
);
}
/**
* 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;
}
}
$StoreAfp = new StoreAfp();
$StoreAfp->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 StoreAfp {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreAfp simulator = new StoreAfp();
//僅限走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 = "1000";
item.amount = "1";
item.total = "1000";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.items = items;
rawData.cost = "1000";
rawData.user_id = "phper";
rawData.user_real_name = "金城武";
rawData.user_zipcode = "407";
rawData.user_address = "台中市西屯區市政路402號5樓之1";
rawData.user_cellphone = "0900000123";
rawData.user_email = "[email protected]";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "AFP";
dynamic info = new ExpandoObject();
info.shipment_type = "2";
info.name = "金城武";
info.tel = "0900000123";
info.zip_code = "432";
info.ship_address = "台中市大肚區磺溪里15號";
rawData.shipping_info = info;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/transaction";
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 StoreAfp {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreAfp simulator = new StoreAfp();
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", "1000");
item.put("amount", "1");
item.put("total", "1000");
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("items", items);
rawData.put("cost", "1000");
rawData.put("user_id", "phper");
rawData.put("user_real_name", "金城武");
rawData.put("user_zipcode", "407");
rawData.put("user_address", "台中市西屯區市政路402號5樓之1");
rawData.put("user_cellphone", "0900000123");
rawData.put("user_email", "[email protected]");
rawData.put("order_id", "1234567890");
rawData.put("ip", "127.0.0.1"); // 此為消費者IP,會做為驗證用
rawData.put("pfn", "AFP");
Map<Object, Object> info = new HashMap<Object, Object>();
info.put("shipment_type", "2");
info.put("name", "金城武");
info.put("tel", "0900000123");
info.put("zip_code", "432");
info.put("ship_address", "台中市大肚區磺溪里15號");
rawData.put("shipping_info", info);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/transaction");
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 StoreAfp() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreAfp.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
items: [{
'id': "1",
'name': "商品名稱",
'cost': "1000",
'amount': "1",
'total': "1000"
}],
cost: "1000",
user_id: "phper",
user_real_name: "金城武",
user_zipcode: "407",
user_address: "台中市西屯區市政路402號5樓之1",
user_cellphone: "0900000123",
user_email: "[email protected]",
order_id: "1234567890",
ip: "127.0.0.1",
pfn: "AFP",
shipping_info : {'shipment_type': 2,
'name': "金城武",
'tel': '0900000123',
'zip_code': '432',
'ship_address': '台中市大肚區磺溪里15號'
}
};
};
/**
* 取得服務位置
*/
StoreAfp.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
StoreAfp.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 到主機
*/
StoreAfp.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();
});
};
/**
* 取得送出欄位資料
*/
StoreAfp.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreAfp.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreAfp = new StoreAfp();
StoreAfp.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 StoreAfp:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'items': [{
'id': "1",
'name': "商品名稱",
'cost': "1000",
'amount': "1",
'total': "1000"
}],
'cost': "1000",
'user_id': "phper",
'user_real_name': "金城武",
'user_zipcode': "407",
'user_address': "台中市西屯區市政路402號5樓之1",
'user_cellphone': "0900000123",
'user_email': "[email protected]",
'order_id': "1234567890",
'ip': "127.0.0.1",
'pfn': "AFP",
'shipping_info': {
'shipment_type': "2",
'name': "金城武",
'tel': "0900000123",
'zip_code': "432",
'ship_address': '台中市大肚區磺溪里15號'
}
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/transaction'
}
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)
StoreAfp = StoreAfp()
StoreAfp.run()
回傳 JSON 結構如下:
{
"code": "284",
"msg": "執行成功。線上訂單成立待出貨-未付款",
"uid": 80998,
"key": "2b4a8cef072c2b8a0b0805d54f54275f",
"finishtime": "",
"cardno": "",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 1000,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"pfn": "AFP",
"trans_type": 1,
"result_type": 4,
"result_content": "{\n \"OrderTotalCost\": \"1000\",\n \"TradeNo\": \"OD2100070580\",\n \"TradeDate\": \"20210225173152\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
「先擁有,後付款」網路購物新態度! 消費者收到繳費通知,九天內至全省四大超商、ATM繳費,消費者確認繳費資訊,再進行繳費。
特約商店『後付款交易』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data | text | 『後付款交易』欄位參考 JSON格式,AES256加密資料 |
『後付款交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string(16) | 特約商店代碼 | 必填 |
cost | string(19) | 訂單總金額 | 必填 |
currency | string(10) | 預設交易幣別(預設為TWD新台幣) | |
order_id | string(50) | 訂單編號 | 必填 |
items | array | 訂單內物品數 | 每筆『商品項目』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
discount | string(19) | 折價金額 (預設0) | |
shipping_fee | string(19) | 運費 | |
user_id | string(50) | 消費者帳號 | |
user_name | string(20) | 消費者姓名 | |
user_real_name | string(20) | 消費者真實姓名 | 必填 |
user_zipcode | string(10) | 消費者郵遞區號 | 必填 |
user_address | string(255) | 消費者地址 | 必填 |
user_sn_type | string(1) | 證號類型 | 『證號類型』值參考 |
user_sn | string(19) | 付款人身分證/統一證號/護照號碼 | |
user_phone | string(20) | 消費者家用電話 | |
user_cellphone_code | string(6) | 消費者行動電話國碼 | |
user_cellphone | string(20) | 消費者行動電話 | 必填 |
user_email | string(80) | 消費者 E-Mail | 必填 |
user_birthday | string(10) | 消費者生日 | |
ip | string(50) | 消費者來源 IP | |
issue_invoice_state | integer(1) | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer(1) | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer(1) | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string(1) | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string(8) | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string(20) | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string(20) | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string(20) | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string(50) | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string(8) | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone | string(10) | 發票郵遞區號 | 當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address | string(200) | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
shipping_info | object | 送貨資訊 | 必填 『後付款送貨資訊』欄位參考 |
『後付款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | 『後付款交易回傳欄位』值參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
後付款請款
<?php
/**
* 特約商店串接-直接交易-後付款請款
*/
final class StoreAfpShipment
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['uid'] = "80747";
$rawData['key'] = "42e46576add74966278a81faf53f732f";
$rawData['shipment_no'] = "9876543210";
$rawData['delivery_code'] = "1";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/afpshipment'
);
}
/**
* 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;
}
}
$StoreAfpShipment = new StoreAfpShipment();
$StoreAfpShipment->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 StoreAfpShipment {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreAfpShipment simulator = new StoreAfpShipment();
//僅限走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.uid = "80747";
rawData.key = "42e46576add74966278a81faf53f732f";
rawData.shipment_no = "9876543210";
rawData.delivery_code = "1";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/afpshipment";
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 StoreAfpShipment {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreAfpShipment simulator = new StoreAfpShipment();
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("uid", "80747");
rawData.put("key", "42e46576add74966278a81faf53f732f");
rawData.put("shipment_no", "9876543210");
rawData.put("delivery_code", "1");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/afpshipment");
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 StoreAfpShipment() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreAfpShipment.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "80747",
key: "42e46576add74966278a81faf53f732f",
shipment_no: "9876543210",
delivery_code: "1"
};
};
/**
* 取得服務位置
*/
StoreAfpShipment.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/afpshipment"
};
};
/**
* AES 256 加密
*/
StoreAfpShipment.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 到主機
*/
StoreAfpShipment.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();
});
};
/**
* 取得送出欄位資料
*/
StoreAfpShipment.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreAfpShipment.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreAfpShipment = new StoreAfpShipment();
StoreAfpShipment.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 StoreAfpShipment:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "80747",
'key': "42e46576add74966278a81faf53f732f",
'shipment_no': "9876543210",
'delivery_code': "1"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/afpshipment'
}
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)
StoreAfpShipment = StoreAfpShipment()
StoreAfpShipment.run()
回傳 JSON 結構如下:
{
"code": "250",
"msg": "執行成功。",
"uid": 80998,
"key": "2b4a8cef072c2b8a0b0805d54f54275f",
"finishtime": "20210225173326",
"cardno": "OD2100070580",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 1000,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"pfn": "AFP",
"trans_type": 1,
"result_type": 4,
"result_content": "{\n \"OrderTotalCost\": 1000,\n \"TradeDate\": \"20210225173326\",\n \"TradeNo\": \"OD2100070580\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
發動後付款交易後,特約商店確認已經出貨給消費者後,即可以發動請款請求。
特約商店『後付款請款』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/afpshipment"} JSON格式,AES256加密資料 |
encry_data | text | 『後付款請款』欄位參考 JSON格式,AES256加密資料 |
『後付款請款』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | |
key | string | 特約商店驗證碼 | |
uid | string(11) | 訂單編號(UID) | |
shipment_no | string | 出貨編號 | |
delivery_code | integer | 貨運商家編號 | 『貨運商家編號』值參考 |
『後付款請款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string(20) | 付費方法 | 『付款方式』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | 『後付款請款交易回傳欄位』值參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
後付款更正出貨
<?php
/**
* 特約商店串接-直接交易-後付款更正出貨
*/
final class StoreAfpShipmentUpdate
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['uid'] = "80747";
$rawData['key'] = "42e46576add74966278a81faf53f732f";
$rawData['shipping_info'] = ['shipment_type' => 2,
'name' => "金城武",
'tel' => '0900000123',
'zip_code' => '432',
'ship_address' => '台中市大肚區磺溪里16號'];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/afpshipmentupdate'
);
}
/**
* 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;
}
}
$StoreAfpShipmentUpdate = new StoreAfpShipmentUpdate();
$StoreAfpShipmentUpdate->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 StoreAfp {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreAfp simulator = new StoreAfp();
//僅限走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.uid = "80747";
rawData.key = "42e46576add74966278a81faf53f732f";
dynamic info = new ExpandoObject();
info.shipment_type = "2";
info.name = "金城武";
info.tel = "0900000123";
info.zip_code = "432";
info.ship_address = "台中市大肚區磺溪里16號";
rawData.shipping_info = info;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/afpshipmentupdate";
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 StoreAfpShipmentUpdate {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreAfpShipmentUpdate simulator = new StoreAfpShipmentUpdate();
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("uid", "80747");
rawData.put("key", "42e46576add74966278a81faf53f732f");
Map<Object, Object> info = new HashMap<Object, Object>();
info.put("shipment_type", "2");
info.put("name", "金城武");
info.put("tel", "0900000123");
info.put("zip_code", "432");
info.put("ship_address", "台中市大肚區磺溪里16號");
rawData.put("shipping_info", info);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/afpshipmentupdate");
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 StoreAfpShipmentUpdate() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreAfpShipmentUpdate.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "80747",
key: "42e46576add74966278a81faf53f732f",
shipping_info: {
shipment_type: "2",
name: "金城武",
tel: "0900000123",
zip_code: "432",
ship_address: "台中市大肚區磺溪里16號"
}
};
};
/**
* 取得服務位置
*/
StoreAfpShipmentUpdate.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/afpshipmentupdate"
};
};
/**
* AES 256 加密
*/
StoreAfpShipmentUpdate.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 到主機
*/
StoreAfpShipmentUpdate.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();
});
};
/**
* 取得送出欄位資料
*/
StoreAfpShipmentUpdate.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreAfpShipmentUpdate.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreAfpShipmentUpdate = new StoreAfpShipmentUpdate();
StoreAfpShipmentUpdate.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 StoreAfpShipmentUpdate:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "80747",
'key': "42e46576add74966278a81faf53f732f",
'shipping_info': {
'shipment_type': "2",
'name': "金城武",
'tel': "0900000123",
'zip_code': "432",
'ship_address': "台中市大肚區磺溪里16號"
}
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/afpshipmentupdate'
}
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)
StoreAfpShipmentUpdate = StoreAfpShipmentUpdate()
StoreAfpShipmentUpdate.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功"
}
發動後付款交易後,在後付款請款發動之前,如出貨資訊資訊不正確,可以發動更正出貨修正出貨資訊。
特約商店『後付款更正出貨』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/afpshipmentupdate"} JSON格式,AES256加密資料 |
encry_data | text | 『後付款更正出貨』欄位參考 JSON格式,AES256加密資料 |
『後付款更正出貨』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | |
key | string | 特約商店驗證碼 | |
uid | string(11) | 訂單編號(UID) | |
shipping_info | object | 送貨資訊 (後付款為必填) | 『後付款送貨資訊』欄位參考 |
『後付款更正出貨』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | |
msg | string(500) | 回傳訊息 |
後付款查詢出貨
<?php
/**
* 特約商店串接-直接交易-後付款查詢出貨
*/
final class StoreAfpShipmentQuery
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['uid'] = "80685";
$rawData['key'] = "35aeb13990e4d57c3735467498e51b6f";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/afpshipmentquery'
);
}
/**
* 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;
}
}
$StoreAfpShipmentQuery = new StoreAfpShipmentQuery();
$StoreAfpShipmentQuery->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 StoreAfpShipmentQuery {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreAfpShipmentQuery simulator = new StoreAfpShipmentQuery();
//僅限走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.uid = "80747";
rawData.key = "42e46576add74966278a81faf53f732f";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/afpshipmentquery";
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 StoreAfpShipmentQuery {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreAfpShipmentQuery simulator = new StoreAfpShipmentQuery();
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("uid", "80747");
rawData.put("key", "42e46576add74966278a81faf53f732f");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/afpshipmentquery");
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 StoreAfpShipmentQuery() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreAfpShipmentQuery.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "80747",
key: "42e46576add74966278a81faf53f732f"
};
};
/**
* 取得服務位置
*/
StoreAfpShipmentQuery.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/afpshipmentquery"
};
};
/**
* AES 256 加密
*/
StoreAfpShipmentQuery.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 到主機
*/
StoreAfpShipmentQuery.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();
});
};
/**
* 取得送出欄位資料
*/
StoreAfpShipmentQuery.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreAfpShipmentQuery.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreAfpShipmentQuery = new StoreAfpShipmentQuery();
StoreAfpShipmentQuery.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 StoreAfpShipmentQuery:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "80747",
'key': "42e46576add74966278a81faf53f732f"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/afpshipmentquery'
}
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)
StoreAfpShipmentQuery = StoreAfpShipmentQuery()
StoreAfpShipmentQuery.run()
回傳 JSON 結構如下:
{
"uid": 80998,
"key": "2b4a8cef072c2b8a0b0805d54f54275f",
"code": "250",
"msg": "付款完成",
"finishtime": "20210225173326",
"trade_no": "OD2100070580",
"order_id": "1234567890",
"user_id": "phper",
"user_real_name": "金城武",
"user_zipcode": "407",
"user_address": "台中市西屯區市政路402號5樓之1",
"user_cellphone": "0900000123",
"user_email": "[email protected]",
"shipping_info": {
"shipment_type": 2,
"company_name": null,
"department_name": null,
"name": "金城武",
"cvs": 0,
"cvs_store": null,
"cvs_store_name": null,
"zip_code": "432",
"ship_address": "台中市大肚區磺溪里15號",
"tel": "0900000123"
},
"shipment_no": "9876543210",
"delivery_code": 1,
"cost": 1000,
"currency": "TWD",
"actual_cost": 1000,
"actual_currency": "TWD",
"pfn": "AFP",
"refund_order": [],
"invoice_state": 0,
"invoice_date": "",
"invoice_wordtrack": "",
"invoice_number": "",
"invoice_rand_code": "",
"invoice_seller_ban": "",
"invoice_buyer_ban": "",
"invoice_left_qrcode": "",
"invoice_middle_barcode": "",
"invoice_right_qrcode": "",
"invoice_title_type": 1,
"invoice_title": "",
"invoice_amount": "0",
"invoice_sales_amount": "0",
"invoice_tax_amount": "0",
"invoice_order_detail": "[]",
"invoice_ratetype": 1,
"invoice_input_type": null,
"invoice_allowance": [],
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
查詢發動後付款交易後的流程,當時候最新的訂單的相關資訊。
特約商店『後付款查詢出貨』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/afpshipmentquery"} JSON格式,AES256加密資料 |
encry_data | text | 『後付款查詢出貨』欄位參考 JSON格式,AES256加密資料 |
『後付款查詢出貨』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | |
uid | string(11) | 訂單編號(UID) | |
key | string | 特約商店驗證碼 |
『後付款查詢出貨』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string(11) | 訂單編號(UID) | |
key | string(50) | 交易驗証碼 | |
code | string | 交易狀態碼 | |
msg | string | 交易狀態訊息 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
trade_no | string | 後付款訂單編號 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
user_real_name | string(20) | 消費者真實姓名 | |
user_zipcode | string(10) | 消費者郵遞區號 | |
user_address | string(255) | 消費者地址 | |
user_cellphone | string(20) | 消費者行動電話 | |
user_email | string(80) | 消費者 E-Mail | |
shipping_info | object | 送貨資訊 | 『後付款送貨資訊』欄位參考 |
shipment_no | string | 出貨編號 | |
delivery_code | integer | 貨運商家編號 | 『貨運商家編號』值參考 |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string | 付費方法 | |
refund_order | array | 退款訂單資訊(多筆格式) | 每筆『後付款出貨查詢-退款資訊』欄位參考 |
invoice_state | integer(2) | 發票開立狀態 | 『電子發票開立狀態類型』值參考 |
invoice_date | string | 發票開立日期(YYYYMMDD) | |
invoice_wordtrack | string(2) | 發票字軌 | |
invoice_number | string(8) | 發票號碼 | |
invoice_rand_code | string(4) | 電子發票隨機碼 | |
invoice_seller_ban | string(8) | 賣方統一編號 | |
invoice_buyer_ban | string(8) | 買方統一編號 | |
invoice_left_qrcode | string(500) | 電子發票左邊QrCode內容 | |
invoice_middle_barcode | string(50) | 電子發票中間Barcode內容(格式Code-39) | |
invoice_right_qrcode | string(1000) | 電子發票右邊QrCode內容 | |
invoice_title_type | integer(1) | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_title | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_amount | string(9) | 電子發票銷售總額 | |
invoice_sales_amount | string(9) | 電子發票銷售額 | |
invoice_tax_amount | string(9) | 電子發票稅額 | |
invoice_order_detail | string(1000) | 電子發票全部產品明細(JSON格式) | 『商品細項』值參考 |
invoice_ratetype | integer(1) | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer(1) | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_allowance | array | 電子發票折讓資訊 | 每筆『電子發票折讓資訊』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
電票交易
<?php
/**
* 特約商店串接-直接交易-電票交易
*/
final class StoreEsvc
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_id'] = "01304161";
$rawData['items'] = [['id' => '1',
'name' => '商品名稱',
'cost' => '1',
'amount' => '1',
'total' => '1']];
$rawData['cost'] = "1";
$rawData['user_id'] = "phper";
$rawData['order_id'] = "1234567890";
$rawData['ip'] = "127.0.0.1";
$rawData['pfn'] = 'ESVC';
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/transaction'
);
}
/**
* 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;
}
}
$StoreEsvc = new StoreEsvc();
$StoreEsvc->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 StoreEsvc {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreEsvc simulator = new StoreEsvc();
//僅限走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 = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.device_id = "01304161";
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1";
rawData.pfn = "ESVC";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/transaction";
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 StoreEsvc {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreEsvc simulator = new StoreEsvc();
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", "1");
item.put("amount", "1");
item.put("total", "1");
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("device_id", "01304161");
rawData.put("items", items);
rawData.put("cost", "1");
rawData.put("user_id", "phper");
rawData.put("order_id", "1234567890");
rawData.put("ip", "127.0.0.1");
rawData.put("pfn", "ESVC");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/transaction");
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 StoreEsvc() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreEsvc.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_id: "01304161",
items: [{
'id': "1",
'name': "商品名稱",
'cost': "1",
'amount': "1",
'total': "1"
}],
cost: "1",
user_id: "phper",
order_id: "1234567890",
ip: "127.0.0.1",
pfn: "ESVC"
};
};
/**
* 取得服務位置
*/
StoreEsvc.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
StoreEsvc.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 到主機
*/
StoreEsvc.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();
});
};
/**
* 取得送出欄位資料
*/
StoreEsvc.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreEsvc.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreEsvc = new StoreEsvc();
StoreEsvc.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 StoreEsvc:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_id': "01304161",
'items': [{
'id': "1",
'name': "商品名稱",
'cost': "1",
'amount': "1",
'total': "1"
}],
'cost': "1",
'user_id': "phper",
'order_id': "1234567890",
'ip': "127.0.0.1",
'pfn': "ESVC"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/transaction'
}
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)
StoreEsvc = StoreEsvc()
StoreEsvc.run()
回傳 JSON 結構如下:
{
"code": "250",
"msg": "執行成功。",
"uid": 83254,
"key": "c6a40103f46c113217e9d631ccfffa05",
"finishtime": "20210331160416",
"cardno": "",
"acode": "",
"order_id": "1234567890",
"user_id": "phper",
"cost": 1,
"currency": "TWD",
"actual_cost": 1,
"actual_currency": "TWD",
"pfn": "EASYCARD",
"trans_type": 1,
"result_type": 4,
"result_content": "{\n \"OrderTotalCost\": \"1\",\n \"TradeDate\": \"20210331160410\",\n \"PaymentType\": \"06\",\n \"Balance\": \"93\",\n \"BeforeBalance\": \"94\",\n \"AutoTopUpAmount\": \"0\",\n \"CardId\": \"658355957\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
請消費者將電子票證放於置讀卡上,然後發動電票交易,即可進行讀卡完成交易。
特約商店直接交易查詢參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data | text | 『電票交易』欄位參考 JSON格式,AES256加密資料 |
『電票交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string(16) | 特約商店代碼 | 必填 |
cost | string(19) | 訂單總金額 | 必填 |
currency | string(10) | 預設交易幣別(預設為TWD新台幣) | |
order_id | string(50) | 訂單編號 | 必填 |
items | array | 訂單內物品數 | 每筆『商品項目』欄位參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 | |
pfn | string | 付費方法 (ESVC) | |
discount | string(19) | 折價金額 (預設0) | |
shipping_fee | string(19) | 運費 | |
user_id | string(50) | 消費者帳號 | |
user_name | string(20) | 消費者姓名 | |
user_real_name | string(20) | 消費者真實姓名 | |
user_zipcode | string(10) | 消費者郵遞區號 | |
user_address | string(255) | 消費者地址 | |
user_sn_type | string(1) | 證號類型 | 『證號類型』值參考 |
user_sn | string(19) | 付款人身分證/統一證號/護照號碼 | |
user_phone | string(20) | 消費者家用電話 | |
user_cellphone_code | string(6) | 消費者行動電話國碼 | |
user_cellphone | string(20) | 消費者行動電話 | |
user_email | string(80) | 消費者 E-Mail | |
user_birthday | string(10) | 消費者生日 | |
ip | string(50) | 消費者來源 IP | |
issue_invoice_state | integer(1) | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer(1) | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer(1) | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string(1) | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string(8) | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string(20) | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string(20) | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string(20) | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string(50) | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string(8) | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone | string(10) | 發票郵遞區號 | 當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address | string(200) | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
device_id | string | 電子票證設備ID |
『電票交易』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string(10) | 交易回傳碼 | |
msg | string(500) | 回傳訊息 | |
uid | string(50) | Payment Hub之交易流水號 | |
key | string(50) | 交易驗証碼 | |
finishtime | string(14) | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string(50) | 銀行端口回傳碼 | |
acode | string(10) | 授權碼 | |
order_id | string(50) | 貴特店系統的訂單編號 | |
user_id | string(50) | 消費者帳號 | |
cost | string(9) | 總交易金額 | |
currency | string(10) | 原交易幣別 | |
actual_cost | string(9) | 實際交易金額 | |
actual_currency | string(10) | 實際交易幣別 | |
pfn | string | 付費方法 | 『電票類型』值參考 |
trans_type | integer(1) | 交易類型 | 『交易類型定義』值參考 |
result_type | string(2) | 回傳結果資料類型 | 『閘道內容回傳格式類型』值參考 |
result_content | string | 回傳結果 | 『電票交易回傳欄位』值參考 |
echo_0 | string(255) | 自訂回傳參數 1 | |
echo_1 | string(255) | 自訂回傳參數 2 | |
echo_2 | string(255) | 自訂回傳參數 3 | |
echo_3 | string(255) | 自訂回傳參數 4 | |
echo_4 | string(255) | 自訂回傳參數 5 |
電票查詢卡號
<?php
/**
* 特約商店串接-直接交易-電票查詢卡號
*/
final class StoreEsvcQueryCardid
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_id'] = "01304161";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/esvcquerycardid'
);
}
/**
* 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;
}
}
$StoreEsvcQueryCardid = new StoreEsvcQueryCardid();
$StoreEsvcQueryCardid->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 StoreEsvcQueryCardid {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreEsvcQueryCardid simulator = new StoreEsvcQueryCardid();
//僅限走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_id = "01304161";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/esvcquerycardid";
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 StoreEsvcQueryCardid {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreEsvcQueryCardid simulator = new StoreEsvcQueryCardid();
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_id", "01304161");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/esvcquerycardid");
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 StoreEsvcQueryCardid() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreEsvcQueryCardid.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_id: "01304161"
};
};
/**
* 取得服務位置
*/
StoreEsvcQueryCardid.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/esvcquerycardid"
};
};
/**
* AES 256 加密
*/
StoreEsvcQueryCardid.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 到主機
*/
StoreEsvcQueryCardid.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();
});
};
/**
* 取得送出欄位資料
*/
StoreEsvcQueryCardid.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreEsvcQueryCardid.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreEsvcQueryCardid = new StoreEsvcQueryCardid();
StoreEsvcQueryCardid.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 StoreEsvcQueryCardid:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_id': "01304161"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/esvcquerycardid'
}
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)
StoreEsvcQueryCardid = StoreEsvcQueryCardid()
StoreEsvcQueryCardid.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"cardid": "658355957"
}
請消費者將電子票證放於置讀卡上,然後發動電票查詢卡號,即可進行讀取卡號資訊。
特約商店電票查詢卡號參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/esvcquerycardid"} JSON格式,AES256加密資料 |
encry_data | text | 『電票查詢卡號』欄位參考 JSON格式,AES256加密資料 |
『電票查詢卡號』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店商務代號 | |
device_id | string | 設備編號 |
『電票查詢卡號』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | |
msg | string(500) | 回傳訊息 | |
cardid | string | 電票卡號 |
電票查詢餘額
<?php
/**
* 特約商店串接-直接交易-電票查詢餘額
*/
final class StoreEsvcQueryBalance
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_id'] = "01304161";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/esvcquerybalance'
);
}
/**
* 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;
}
}
$StoreEsvcQueryBalance = new StoreEsvcQueryBalance();
$StoreEsvcQueryBalance->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 StoreEsvcQueryBalance {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreEsvcQueryBalance simulator = new StoreEsvcQueryBalance();
//僅限走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_id = "01304161";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/esvcquerybalance";
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 StoreEsvcQueryBalance {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://ka.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreEsvcQueryBalance simulator = new StoreEsvcQueryBalance();
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_id", "01304161");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/esvcquerybalance");
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 StoreEsvcQueryBalance() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://ka.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreEsvcQueryBalance.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
device_id: "01304161"
};
};
/**
* 取得服務位置
*/
StoreEsvcQueryBalance.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/esvcquerybalance"
};
};
/**
* AES 256 加密
*/
StoreEsvcQueryBalance.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 到主機
*/
StoreEsvcQueryBalance.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();
});
};
/**
* 取得送出欄位資料
*/
StoreEsvcQueryBalance.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreEsvcQueryBalance.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreEsvcQueryBalance = new StoreEsvcQueryBalance();
StoreEsvcQueryBalance.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 StoreEsvcQueryBalance:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://ka.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'device_id': "01304161"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/esvcquerybalance'
}
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)
StoreEsvcQueryBalance = StoreEsvcQueryBalance()
StoreEsvcQueryBalance.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"balance": 93,
"cardid": "658355957",
"cardbrand": "EASYCARD"
}
請消費者將電子票證放於置讀卡上,然後發動電票查詢餘額,即可進行讀取電子票證之餘額以及卡別資訊。
特約商店電票查詢餘額參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/esvcquerycardid"} JSON格式,AES256加密資料 |
encry_data | text | 『電票查詢餘額』欄位參考 JSON格式,AES256加密資料 |
『電票查詢餘額』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店商務代號 | |
device_id | string | 設備編號 |
『電票查詢餘額』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | |
msg | string(500) | 回傳訊息 | |
balance | float | 餘額 | |
cardid | string | 電票卡號 | |
cardbrand | string | 電票類型 | 『電票類型』值參考 |
電票退款
<?php
/**
* 特約商店串接-直接交易-電票退款
*/
final class StoreEsvcRefund
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://ka.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['device_id'] = "01304161";
$rawData['uid'] = "83091";
$rawData['key'] = "f4823bd2c2a462eaece7b7c25d0f4470";
$rawData['cost'] = 1;
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/esvcrefund'
);
}
/**
* 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;
}
}
$StoreEsvcRefund = new StoreEsvcRefund();
$StoreEsvcRefund->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 StoreEsvcRefund {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://ka.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreEsvcRefund simulator = new StoreEsvcRefund();
//僅限走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_id = "01304161";
rawData.uid = "83091";
rawData.key = "f4823bd2c2a462eaece7b7c25d0f4470";
rawData.cost = 1;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/esvcrefund";
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>
/// A