OCP 電子錢包設計概要
電子錢包簡介
電子錢包用於商家的網頁上
(支援PassKey安全認證,此功能不支援 web view,請知悉)
安全性設計
所有的 API 發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES加密,再透過HTTPS加密傳輸。交易資料不由消費者端送出,可確保資料不被消費者竄改。
資料驗證
交易參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。
特約商店只需確保與 OCP 連線正常,即可確保服務正常。
系統架構
OCP 目前提供之服務與格式
OCP 提供多種交易與應用方式,應用情境如下:
(1)電子錢包綁定:綁定信用卡至 OCP 電子錢包。
(2)線下掃碼QRCODE:消費者出示付款碼。
(3)線下掃碼收款:消費者顯示付款碼後,可以進行掃碼發動交易。
(4)線上直接交易:消費者在選擇電子錢包支付後,可以直接在手機完成交易
(5)線上購物交易:消費者在選擇電子錢包支付後,可查看購物內容並在手機完成交易
(6)實名驗證:消費者由店家決定的驗證方式進行實名驗證
介接網址
經銷商模式
位置 |
API介接網址 |
測試區 |
https://pay.usecase.cc/api/agent |
正式區 |
https://ka.mypay.tw/api/agent |
資料加密方式
AES 256編碼 + base64編碼(附錄二資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
電子錢包綁定與交易
<?php
/**
* 經銷商串接-電子錢包綁定與交易
*/
final class AgentEWallet
{
/**
* 經銷商商務代號
* @var string
*/
public $agentUid = "518169081001";
/**
* 經銷商金鑰或認證碼
* @var string
*/
public $agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/**
* 特約商店商務代號(代特約商店發動)
* @var string
*/
public $storeUid = "289151880002";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/agent";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['user_id'] = "userid";
$rawData['type'] = "bind";
$rawData['return_url'] = "";
$rawData['homepage_url'] = "";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'ocpap',
'cmd' => 'api/ewallet'
);
}
/**
* 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['agent_uid'] = $this->agentUid;
$postData['service'] = $this->encrypt($this->getService(), $this->agentKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->agentKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$AgentEWallet = new AgentEWallet();
$AgentEWallet->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 AgentEWallet {
/// <summary>
/// 經銷商商務代號
/// </summary>
public string agentUid = "518169081001";
/// <summary>
/// 經銷商金鑰或認證碼
/// </summary>
public string agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/agent";
/// <summary>
/// 執行
/// </summary>
static void Main() {
AgentEWallet simulator = new AgentEWallet();
//僅限走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.user_id = "userid";
rawData.type = "bind";
rawData.return_url = "";
rawData.homepage_url = "";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "ocpap";
rawData.cmd = "api/ewallet";
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.agentKey, IV);
var svr_encode = Encrypt(svr_json, this.agentKey, 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["agent_uid"] = this.agentUid;
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 AgentEWallet {
/**
* 經銷商商務代號
*/
String agentUid = "518169081001";
/**
* 經銷商金鑰或認證碼
*/
String agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/agent";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
AgentEWallet simulator = new AgentEWallet();
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("user_id", "userid");
rawData.put("type", "bind");
rawData.put("return_url", "");
rawData.put("homepage_url", "");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "ocpap");
rawData.put("cmd", "api/ewallet");
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.agentKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.agentKey), "UTF-8");
postData = "agent_uid=" + this.agentUid + "&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 AgentEWallet() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWallet.prototype.getRawData = function () {
return {
user_id: "userid",
type: "bind",
return_url: "",
homepage_url: ""
};
};
/**
* 取得服務位置
*/
AgentEWallet.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/ewallet"
};
};
/**
* AES 256 加密
*/
AgentEWallet.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 到主機
*/
AgentEWallet.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWallet.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWallet.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
AgentEWallet = new AgentEWallet();
AgentEWallet.run();
# -*- coding: utf-8 -*-
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""經銷商串接-電子錢包綁定與交易
"""
class AgentEWallet:
# 經銷商商務代號
agentUid = "518169081001";
# 經銷商金鑰或認證碼
agentKey = b"2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
# 特約商店商務代號
storeUid = "289151880002"
# 串接交易位置
url = "https://pay.usecase.cc/api/agent"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'user_id': "userid",
'type': "bind",
'return_url': "",
'homepage_url': "",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'ocpap',
'cmd': 'api/ewallet'
}
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 = {
'agent_uid': self.agentUid,
'service': self.encrypt(self.getService(), self.agentKey),
'encry_data': self.encrypt(self.getRawData(), self.agentKey)
}
return postData
def run(self):
"""執行
"""
json = self.post(self.getPostData())
print(json)
AgentEWallet = AgentEWallet()
AgentEWallet.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "",
"url": "https:\/\/ocp.usecase.cc\/open\/bd14e2f1cfd84b5891c6dcd491eda7c9"
}
特約商店『電子錢包綁定與交易』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/ewallet"} JSON格式,AES256加密資料 |
encry_data |
text |
『電子錢包綁定與交易』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
user_id |
string |
使用者帳號,不區分大小寫,例如l12和L12是一樣的 |
必填 |
type |
string |
電子錢包交易類型 |
必填 『電子錢包交易類型』值參考 |
return_url |
string |
交易結果通知網址 |
|
homepage_url |
string |
返回首頁連結 |
必填 |
『電子錢包綁定與交易』回傳欄位
參數名稱 |
型態 |
說明 |
必須 |
code |
string |
回傳碼 |
『電子錢包執行狀態碼』值參考 |
msg |
string |
回傳訊息 |
|
url |
string |
錢包開啟網址 |
|
『電子錢包交易/退款完成通知』回報欄位
參數名稱 |
型態 |
說明 |
必須 |
user_id |
string |
消費者帳號 |
|
code |
string |
交易狀態 |
|
msg |
string |
交易訊息(付款完成、退款完成) |
|
actual_cost |
string |
實際交易金額 |
|
actual_currency |
string |
實際交易幣別 |
|
finishtime |
string |
交易完成時間(YYYYMMDDHHmmss) |
|
store_uid |
string |
消費者交易之特約商店商務代號 |
|
store_name |
string |
消費者交易之特約商店名稱 |
|
order_id |
string |
消費者交易之特約商店的訂單編號 |
|
items |
array |
消費者交易之商品名稱 |
每筆『商品項目』欄位參考 |
值 |
型態 |
說明 |
備註 |
bind |
string |
綁卡 |
|
pay |
string |
交易 |
|
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
線下掃碼收款
<?php
/**
* 經銷商串接-線下掃碼收款
*/
final class AgentOffline
{
/**
* 經銷商商務代號
* @var string
*/
public $agentUid = "518169081001";
/**
* 經銷商金鑰或認證碼
* @var string
*/
public $agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/**
* 特約商店商務代號(代特約商店發動)
* @var string
*/
public $storeUid = "289151880002";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/agent";
/**
* 取得串接欄位資料
* @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";
$rawData['pfn'] = "OFFLINE";
$rawData['data_json'] = "{\"qrCode\":\"MY13697573pbmQsYhY\"}";
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['agent_uid'] = $this->agentUid;
$postData['service'] = $this->encrypt($this->getService(), $this->agentKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->agentKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$AgentOffline = new AgentOffline();
$AgentOffline->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 AgentOffline {
/// <summary>
/// 經銷商商務代號
/// </summary>
public string agentUid = "518169081001";
/// <summary>
/// 經銷商金鑰或認證碼
/// </summary>
public string agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/agent";
/// <summary>
/// 執行
/// </summary>
static void Main() {
AgentOffline simulator = new AgentOffline();
//僅限走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";
rawData.pfn = "OFFLINE";
rawData.data_json = "{\"qrCode\":\"MY13697573pbmQsYhY\"}";
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.agentKey, IV);
var svr_encode = Encrypt(svr_json, this.agentKey, 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["agent_uid"] = this.agentUid;
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 AgentOffline {
/**
* 經銷商商務代號
*/
String agentUid = "518169081001";
/**
* 經銷商金鑰或認證碼
*/
String agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/agent";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
AgentOffline simulator = new AgentOffline();
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");
rawData.put("pfn", "OFFLINE");
rawData.put("data_json", "{\"qrCode\":\"MY13697573pbmQsYhY\"}");
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.agentKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.agentKey), "UTF-8");
postData = "agent_uid=" + this.agentUid + "&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 AgentOffline() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentOffline.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: "OFFLINE",
data_json: "{\"qrCode\":\"MY13697573pbmQsYhY\"}",
};
};
/**
* 取得服務位置
*/
AgentOffline.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/transaction"
};
};
/**
* AES 256 加密
*/
AgentOffline.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 到主機
*/
AgentOffline.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();
});
};
/**
* 取得送出欄位資料
*/
AgentOffline.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentOffline.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
AgentOffline = new AgentOffline();
AgentOffline.run();
# -*- coding: utf-8 -*-
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""經銷商串接-線下掃碼收款
"""
class AgentOffline:
# 經銷商商務代號
agentUid = "518169081001";
# 經銷商金鑰或認證碼
agentKey = b"2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
# 特約商店商務代號
storeUid = "289151880002"
# 串接交易位置
url = "https://pay.usecase.cc/api/agent"
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': "OFFLINE",
'data_json': "{\"qrCode\":\"MY13697573pbmQsYhY\"}",
}
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 = {
'agent_uid': self.agentUid,
'service': self.encrypt(self.getService(), self.agentKey),
'encry_data': self.encrypt(self.getRawData(), self.agentKey)
}
return postData
def run(self):
"""執行
"""
json = self.post(self.getPostData())
print(json)
AgentOffline = AgentOffline()
AgentOffline.run()
回傳 JSON 結構如下:
{
"invoice_state": 0,
"invoice_state_msg": "不處理",
"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_print_type": 0,
"invoice_print_device": 0,
"invoice_amount": "",
"invoice_sales_amount": "",
"invoice_tax_amount": "",
"invoice_order_detail": "",
"invoice_ratetype": 1,
"invoice_input_type": "",
"invoice_cloud_type": "",
"invoice_mobile_code": "",
"invoice_tax_id": "",
"invoice_natural_person": "",
"invoice_m_post_zone": "",
"invoice_m_address": "",
"invoice_love_code": "",
"invoice_b2b_title": "",
"invoice_b2b_id": "",
"invoice_b2b_post_zone": "",
"invoice_b2b_address": "",
"code": "250",
"msg": "執行成功。",
"uid": 96389,
"key": "501a972113307389f1391475c36d489b",
"finishtime": "20211108160720",
"cardno": "400361******7729",
"acode": "211923",
"card_type": "1",
"issuing_bank": "中國信託",
"issuing_bank_uid": "822",
"transaction_mode": "1",
"supplier_name": "中國信託商業銀行",
"supplier_code": "B8",
"order_id": "1234567890",
"user_id": "phper",
"cost": 10,
"currency": "TWD",
"actual_cost": 10,
"actual_currency": "TWD",
"pfn": "CREDITCARD",
"trans_type": 1,
"result_type": 4,
"result_content_type": "CREDITCARD",
"result_content": "{\n \"ACode\": \"211923\",\n \"TradeNo\": \"96389\",\n \"Status\": 0,\n \"OrderTotalCost\": \"10\"\n}",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
特約商店『線下掃碼收款』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "api", "cmd": "api\/transaction"} JSON格式,AES256加密資料 |
encry_data |
text |
『線下掃碼收款』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
store_uid |
string |
特約商店代碼 |
必填 |
cost |
string |
訂單總金額 |
必填 |
currency |
string |
預設交易幣別(預設為TWD新台幣) |
|
order_id |
string |
訂單編號 |
必填 |
items |
array |
訂單內物品數 |
每筆『商品項目』欄位參考 |
echo_0 |
string |
自訂回傳參數 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 |
消費者真實姓名 |
|
user_english_name |
string |
消費者英文名稱 |
|
user_zipcode |
string |
消費者郵遞區號 |
|
user_address |
string |
消費者地址 |
|
user_sn_type |
string |
證號類型 |
『證號類型』值參考 |
user_sn |
string |
付款人身分證/統一證號/護照號碼 |
|
user_phone |
string |
消費者家用電話 |
|
user_cellphone_code |
string |
消費者行動電話國碼 |
|
user_cellphone |
string |
消費者行動電話 |
|
user_email |
string |
消費者 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_m_post_zone |
string |
EMail 紙本寄送郵遞區號 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_m_address |
string |
EMail 紙本寄送住址 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_love_code |
string |
愛心碼 |
當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title |
string |
發票抬頭 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id |
string |
統一編號 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone |
string |
發票郵遞區號 |
當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address |
string |
發票地址 |
當invoice_input_type為3時,此欄位才有效 |
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 |
『是否為經銷商代收費模式』值參考 |
data_json |
string |
因pfn需要帶入之json資料 OFFLINE |
|
『線下掃碼收款』回傳欄位
參數名稱 |
型態 |
說明 |
必須 |
code |
string |
交易回傳碼 |
|
msg |
string |
回傳訊息 |
|
uid |
string |
Payment Hub之交易流水號 |
|
key |
string |
交易驗証碼 |
|
finishtime |
string |
交易完成時間(YYYYMMDDHHmmss) |
|
cardno |
string |
銀行端口回傳碼 |
|
acode |
string |
授權碼 |
|
card_type |
integer |
信用卡卡別 |
『信用卡別類型』值參考 |
issuing_bank |
string |
發卡行 |
|
issuing_bank_uid |
string |
發卡銀行代碼 |
|
transaction_mode |
integer |
交易服務類型 |
『交易服務類型』值參考 |
supplier_name |
string |
交易之金融服務商 |
|
supplier_code |
string |
交易之金融服務商代碼 |
|
order_id |
string |
貴特店系統的訂單編號 |
|
user_id |
string |
消費者帳號 |
|
cost |
string |
總交易金額 |
|
currency |
string |
原交易幣別 |
|
actual_cost |
string |
實際交易金額 |
|
actual_currency |
string |
實際交易幣別 |
|
pfn |
string |
付費方法 |
『付款方式』值參考 |
trans_type |
integer |
交易類型 |
『交易類型定義』值參考 |
result_type |
string |
回傳結果資料類型 |
『閘道內容回傳格式類型』值參考 |
result_content_type |
string |
回傳資料內容類型 |
『資料內容所屬支付名稱』值參考 |
result_content |
string |
回傳結果 |
|
echo_0 |
string |
自訂回傳參數 1 |
|
echo_1 |
string |
自訂回傳參數 2 |
|
echo_2 |
string |
自訂回傳參數 3 |
|
echo_3 |
string |
自訂回傳參數 4 |
|
echo_4 |
string |
自訂回傳參數 5 |
|
invoice_state |
integer |
發票開立狀態 |
『電子發票開立狀態類型』值參考 |
invoice_state_msg |
string |
發票開立狀態訊息 |
|
invoice_date |
string |
發票開立日期(YYYYMMDDHHmmss) |
|
invoice_wordtrack |
string |
發票字軌 |
|
invoice_number |
string |
發票號碼 |
|
invoice_rand_code |
string |
電子發票隨機碼 |
|
invoice_seller_ban |
string |
賣方統一編號 |
|
invoice_buyer_ban |
string |
買方統一編號 |
|
invoice_left_qrcode |
string |
電子發票左邊QrCode內容 |
|
invoice_middle_barcode |
string |
電子發票中間Barcode內容(格式Code-39) |
|
invoice_right_qrcode |
string |
電子發票右邊QrCode內容 |
|
invoice_title_type |
integer |
電子發票列印標題格式 |
『電子發票紙本列印標題類型』值參考 |
invoice_title |
string |
電子發票列印標題格式 |
|
invoice_print_type |
integer |
電子發票列印類型 |
『電子發票列印類型』值參考 |
invoice_print_device |
integer |
電子發票列印設備 |
『電子發票列印設備』值參考 |
invoice_amount |
string |
電子發票銷售總額 |
|
invoice_sales_amount |
string |
電子發票銷售額 |
|
invoice_tax_amount |
string |
電子發票稅額 |
|
invoice_order_detail |
string |
電子發票全部產品明細(JSON格式) |
『商品細項』值參考 |
invoice_ratetype |
integer |
電子發票稅率別 |
『電子發票稅率別』值參考 |
invoice_input_type |
integer |
電子發票開立類型 |
『電子發票開立類型』值參考 |
invoice_cloud_type |
string |
電子發票開立類型-雲端發票類型 |
『雲端發票類型』值參考 |
invoice_mobile_code |
string |
當invoice_cloud_type為2時紀錄的手機條碼 |
|
invoice_tax_id |
string |
當invoice_cloud_type為2時紀錄的統一編號 |
|
invoice_natural_person |
string |
當invoice_cloud_type為3時紀錄的自然人憑證條碼 |
|
invoice_m_post_zone |
string |
當invoice_cloud_type為4時紀錄中獎時紙本發票郵遞區號 |
|
invoice_m_address |
string |
當invoice_cloud_type為4時紀錄中獎時紙本發票收件住址 |
|
invoice_love_code |
string |
當invoice_input_type為2時紀錄的愛心碼 |
|
invoice_b2b_title |
string |
當invoice_input_type為3時紀錄的發票抬頭 |
|
invoice_b2b_id |
string |
當invoice_input_type為3時紀錄的統一編號 |
|
invoice_b2b_post_zone |
string |
當invoice_input_type為3時紀錄的郵遞區號 |
|
invoice_b2b_address |
string |
當invoice_input_type為3時紀錄的發票地址 |
|
值 |
型態 |
說明 |
備註 |
OFFLINE |
string |
電子錢包線下QRCODE |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
身份證字號(預設) |
|
2 |
integer |
統一證號 |
|
3 |
integer |
護照號碼 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
不開立電子發票 |
|
1 |
integer |
開立電子發票 |
|
2 |
integer |
依系統設定(預設) |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
應稅(預設) |
|
2 |
integer |
零稅率 |
|
3 |
integer |
免稅 |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
雲端發票 |
|
2 |
integer |
發票捐贈 |
|
3 |
integer |
實體發票 |
|
值 |
型態 |
說明 |
備註 |
2 |
integer |
手機條碼 |
|
3 |
integer |
自然人憑證條碼 |
|
4 |
integer |
以E-Mail寄送 |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
含手續費 |
|
0 |
integer |
不含手續費(預設) |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
含手續費 |
|
0 |
integer |
不含手續費(預設) |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
是經銷商代收費模式 |
|
0 |
integer |
不是經銷商代收費模式 |
|
值 |
型態 |
說明 |
備註 |
0 |
string |
無法辨識或支付方式為非信用卡類 |
|
1 |
string |
VISA |
|
2 |
string |
MasterCard |
|
3 |
string |
JCB |
|
4 |
string |
AMEX |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
尚未進行閘道交易 |
|
1 |
integer |
代收代付 |
|
2 |
integer |
特店模式 |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
一般 (預設) |
|
2 |
integer |
分期 |
|
3 |
integer |
紅利 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
無法辨識 |
|
1 |
integer |
網址 |
|
2 |
integer |
超連結本文 |
|
3 |
integer |
xml |
|
4 |
integer |
json |
|
5 |
integer |
csv |
|
6 |
integer |
串流 (咦?) |
|
值 |
型態 |
說明 |
備註 |
E_COLLECTION |
string |
虛擬帳號 |
|
IBON |
string |
iBON |
|
FAMIPORT |
string |
FamiPort |
|
LIFEET |
string |
LIFE-ET |
|
WEBATM |
string |
WEBATM |
|
CREDITCARD |
string |
信用卡 |
|
UNIONPAY |
string |
銀聯卡 |
|
SVC |
string |
點數卡(GASH ,Imoney) |
|
ABROAD |
string |
海外信用卡 |
|
ALIPAY |
string |
支付寶 |
|
WECHAT |
string |
微信支付 |
|
LINEPAYON |
string |
LINE Pay線上付款 |
|
LINEPAYOFF |
string |
LINE Pay線下付款 |
|
WECHATOFF |
string |
微信支付線下 |
|
APPLEPAY |
string |
APPLE PAY |
|
GOOGLEPAY |
string |
Google Pay |
|
EACH |
string |
eACH交易 |
|
CARDLESS |
string |
無卡分期 |
|
PION |
string |
Pi 拍錢包線上 |
|
PIOFF |
string |
Pi 拍錢包線下 |
|
AMEX |
string |
美國運通 |
|
JKOON |
string |
街口支付線上 |
|
JKOOFF |
string |
街口支付線下 |
|
ALIPAYOFF |
string |
支付寶線下 |
|
M_RECHARGE |
string |
儲值交易 |
|
EASYWALLETON |
string |
悠遊付線上 |
|
EASYWALLETOFF |
string |
悠遊付線下 |
|
AFP |
string |
後付款 |
|
BARCODE |
string |
超商條碼繳費 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
不處理或已無效(預設) |
|
1 |
integer |
等候處理中 |
|
2 |
integer |
發票開立成功 |
|
3 |
integer |
發票開立失敗(系統或特約商店發票相關設定不正確) |
|
4 |
integer |
作癈 |
|
5 |
integer |
發票開立失敗(系統發生錯誤) |
|
6 |
integer |
折讓 |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
文字 |
|
2 |
integer |
圖形(圖片網址) |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
不列印 自行處置 |
|
1 |
integer |
列印 電子發票 + 商品明細 |
|
2 |
integer |
只印電子發票 |
|
3 |
integer |
只印商品明細 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
自行處理 |
|
1 |
integer |
SUNMI V2 PRO |
|
參數名稱 |
型態 |
說明 |
必須 |
Description |
string |
商品名稱 |
必填 |
Quantity |
string |
數量 |
必填 |
UnitPrice |
string |
單價 |
必填 |
Amount |
string |
總金額 |
必填 |
線上直接交易
const crypto = require('crypto');
const httpRequest = require('https');
const { v4: uuidv4 } = require("uuid");
/**
* 經銷商串接-線上直接交易
*/
function AgentEWallet() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWallet.prototype.getRawData = function () {
return {
"store_uid": "289151880002",
"cost": "210",
"currency": "TWD",
"order_id": "1234567890",
"items": [
{
"id": "1",
"name": "商品名稱",
"cost": 20,
"amount": 1,
"total": 20
},
{
"id": "2",
"name": "刷刷跳跳糖",
"cost": 20,
"amount": 5,
"total": 100
},
{
"id": "3",
"name": "小飛仔",
"cost": 45,
"amount": 2,
"total": 90
}
],
"user_id": "userid",
"ip": "127.0.0.1"
};
};
/**
* 取得服務位置
*/
AgentEWallet.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/directdeal"
};
};
/**
* AES 256 加密
*/
AgentEWallet.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 到主機
*/
AgentEWallet.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWallet.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWallet.prototype.run = async function () {
json = await this.post(this.getPostData())
let file = require("fs");
file.writeFileSync("./tmp.json", json);
json = require("./tmp.json");
json.url = json.url.replace(/\\/g, "");
console.log(json);
};
/* 開啟電子錢包 */
AgentEWallet.prototype.ewallet = async function (raw_data) {
let {
user_id,
type,
return_url
} = raw_data;
let res = await this.post({
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt({
user_id: user_id,
type: type,
return_url: return_url
}, this.agentKey)
});
let file = require("fs");
let tmp_name = uuidv4() + ".json";
file.writeFileSync(`/tmp/${tmp_name}`, res);
res = require(`/tmp/${tmp_name}`);
res.url = res.url.replace(/\\/g, "");
return res;
}
exports.ewallet_agent = new AgentEWallet();
經銷商『線上直接交易』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/directdeal"} JSON格式,AES256加密資料 |
encry_data |
text |
『線上直接交易』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
store_uid |
string |
特約商店代碼 |
必填 |
cost |
string |
訂單總金額 |
必填 |
currency |
string |
預設交易幣別(預設為TWD新台幣) |
|
order_id |
string |
訂單編號 |
必填 |
items |
array |
訂單內物品數 |
每筆『商品項目』欄位參考 |
echo_0 |
string |
自訂回傳參數 1 |
|
echo_1 |
string |
自訂回傳參數 2 |
|
echo_2 |
string |
自訂回傳參數 3 |
|
echo_3 |
string |
自訂回傳參數 4 |
|
echo_4 |
string |
自訂回傳參數 5 |
|
user_id |
string |
消費者帳號,不區分大小寫,例如l12和L12是一樣的 |
必填 |
user_name |
string |
消費者姓名 |
|
user_real_name |
string |
消費者真實姓名 |
|
user_english_name |
string |
消費者英文名稱 |
|
user_zipcode |
string |
消費者郵遞區號 |
|
user_address |
string |
消費者地址 |
|
user_sn_type |
string |
證號類型 |
『證號類型』值參考 |
user_sn |
string |
付款人身分證/統一證號/護照號碼 |
|
user_phone |
string |
消費者家用電話 |
|
user_cellphone_code |
string |
消費者行動電話國碼 |
|
user_cellphone |
string |
消費者行動電話 |
|
user_email |
string |
消費者 E-Mail |
|
user_birthday |
string |
消費者生日 |
|
ip |
string |
消費者來源 IP |
必填 |
return_url |
string |
交易結果背景通知網址 |
|
success_returl |
string |
交易成功導頁 |
|
failure_returl |
string |
交易失敗導頁 |
|
homepage_url |
string |
首頁網址 |
使用者於介面點選首頁按鈕即導回該頁面 |
issue_invoice_state |
integer |
開立發票 |
『電子發票是否開立狀態』值參考 |
invoice_ratetype |
integer |
電子發票稅率別 |
『電子發票稅率別』值參考 |
invoice_input_type |
integer |
電子發票開立類型 |
『電子發票開立類型』值參考 |
invoice_cloud_type |
integer |
「雲端發票」類型 |
當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_m_post_zone |
string |
EMail 紙本寄送郵遞區號 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_m_address |
string |
EMail 紙本寄送住址 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_love_code |
string |
愛心碼 |
當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title |
string |
發票抬頭 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id |
string |
統一編號 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone |
string |
發票郵遞區號 |
當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address |
string |
發票地址 |
當invoice_input_type為3時,此欄位才有效 |
『線上直接交易』回傳欄位
參數名稱 |
型態 |
說明 |
必須 |
code |
string |
回傳碼 |
『電子錢包執行狀態碼』值參考 |
msg |
string |
回傳訊息 |
|
url |
string |
錢包開啟網址 |
|
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
線上購物交易
const crypto = require('crypto');
const httpRequest = require('https');
const { v4: uuidv4 } = require("uuid");
/**
* 經銷商串接-線上購物交易
*/
function AgentEWallet() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWallet.prototype.getRawData = function () {
return {
"store_uid": "289151880002",
"cost": "210",
"currency": "TWD",
"order_id": "1234567890",
"items": [
{
"id": "1",
"name": "商品名稱",
"cost": 20,
"amount": 1,
"total": 20
},
{
"id": "2",
"name": "刷刷跳跳糖",
"cost": 20,
"amount": 5,
"total": 100
},
{
"id": "3",
"name": "小飛仔",
"cost": 45,
"amount": 2,
"total": 90
}
],
"user_id": "userid",
"ip": "127.0.0.1"
};
};
/**
* 取得服務位置
*/
AgentEWallet.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/directdeal"
};
};
/**
* AES 256 加密
*/
AgentEWallet.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 到主機
*/
AgentEWallet.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWallet.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWallet.prototype.run = async function () {
json = await this.post(this.getPostData())
let file = require("fs");
file.writeFileSync("./tmp.json", json);
json = require("./tmp.json");
json.url = json.url.replace(/\\/g, "");
console.log(json);
};
/* 開啟電子錢包 */
AgentEWallet.prototype.ewallet = async function (raw_data) {
let {
user_id,
type,
return_url
} = raw_data;
let res = await this.post({
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt({
user_id: user_id,
type: type,
return_url: return_url
}, this.agentKey)
});
let file = require("fs");
let tmp_name = uuidv4() + ".json";
file.writeFileSync(`/tmp/${tmp_name}`, res);
res = require(`/tmp/${tmp_name}`);
res.url = res.url.replace(/\\/g, "");
return res;
}
exports.ewallet_agent = new AgentEWallet();
經銷商『線上購物交易』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/shoppingdeal"} JSON格式,AES256加密資料 |
encry_data |
text |
『線上購物交易』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
store_uid |
string |
特約商店代碼 |
必填 |
cost |
string |
訂單總金額 |
必填 |
currency |
string |
預設交易幣別(預設為TWD新台幣) |
|
order_id |
string |
訂單編號 |
必填 |
items |
array |
訂單內物品數 |
每筆『商品項目』欄位參考 |
echo_0 |
string |
自訂回傳參數 1 |
|
echo_1 |
string |
自訂回傳參數 2 |
|
echo_2 |
string |
自訂回傳參數 3 |
|
echo_3 |
string |
自訂回傳參數 4 |
|
echo_4 |
string |
自訂回傳參數 5 |
|
user_id |
string |
消費者帳號,不區分大小寫,例如l12和L12是一樣的 |
必填 |
user_name |
string |
消費者姓名 |
|
user_real_name |
string |
消費者真實姓名 |
|
user_english_name |
string |
消費者英文名稱 |
|
user_zipcode |
string |
消費者郵遞區號 |
|
user_address |
string |
消費者地址 |
|
user_sn_type |
string |
證號類型 |
『證號類型』值參考 |
user_sn |
string |
付款人身分證/統一證號/護照號碼 |
|
user_phone |
string |
消費者家用電話 |
|
user_cellphone_code |
string |
消費者行動電話國碼 |
|
user_cellphone |
string |
消費者行動電話 |
|
user_email |
string |
消費者 E-Mail |
|
user_birthday |
string |
消費者生日 |
|
ip |
string |
消費者來源 IP |
必填 |
return_url |
string |
交易結果背景通知網址 |
|
success_returl |
string |
交易成功導頁 |
|
failure_returl |
string |
交易失敗導頁 |
|
homepage_url |
string |
首頁網址 |
使用者於介面點選首頁按鈕即導回該頁面 |
issue_invoice_state |
integer |
開立發票 |
『電子發票是否開立狀態』值參考 |
invoice_ratetype |
integer |
電子發票稅率別 |
『電子發票稅率別』值參考 |
invoice_input_type |
integer |
電子發票開立類型 |
『電子發票開立類型』值參考 |
invoice_cloud_type |
integer |
「雲端發票」類型 |
當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_m_post_zone |
string |
EMail 紙本寄送郵遞區號 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_m_address |
string |
EMail 紙本寄送住址 |
當invoice_cloud_type為4,此欄位才有效,非必須 |
invoice_love_code |
string |
愛心碼 |
當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title |
string |
發票抬頭 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id |
string |
統一編號 |
當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone |
string |
發票郵遞區號 |
當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address |
string |
發票地址 |
當invoice_input_type為3時,此欄位才有效 |
『線上購物交易』回傳欄位
參數名稱 |
型態 |
說明 |
必須 |
code |
string |
回傳碼 |
『電子錢包執行狀態碼』值參考 |
msg |
string |
回傳訊息 |
|
url |
string |
錢包開啟網址 |
|
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
實名驗證
const crypto = require('crypto');
const httpRequest = require('https');
const { v4: uuidv4 } = require("uuid");
/**
* 經銷商串接-實名驗證
*/
function AgentEWallet() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWallet.prototype.getRawData = function () {
return {
"country_code":"886",
"store_uid": "289151880002",
"method_type": "1",
"user_id": "userid",
"user_cellphone": "912345678",
"user_sn_type": "1",
"user_sn": "A123456789",
"bank_code": "426",
"tix": "560",
"ip": "127.0.0.1"
};
};
/**
* 取得服務位置
*/
AgentEWallet.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/verification"
};
};
/**
* AES 256 加密
*/
AgentEWallet.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 到主機
*/
AgentEWallet.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWallet.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWallet.prototype.run = async function () {
json = await this.post(this.getPostData())
let file = require("fs");
file.writeFileSync("./tmp.json", json);
json = require("./tmp.json");
json.url = json.url.replace(/\\/g, "");
console.log(json);
};
/* 開啟電子錢包 */
AgentEWallet.prototype.ewallet = async function (raw_data) {
let {
user_id,
type,
return_url
} = raw_data;
let res = await this.post({
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt({
user_id: user_id,
type: type,
return_url: return_url
}, this.agentKey)
});
let file = require("fs");
let tmp_name = uuidv4() + ".json";
file.writeFileSync(`/tmp/${tmp_name}`, res);
res = require(`/tmp/${tmp_name}`);
res.url = res.url.replace(/\\/g, "");
return res;
}
exports.ewallet_agent = new AgentEWallet();
經銷商『實名驗證』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/verification"} JSON格式,AES256加密資料 |
encry_data |
text |
『實名驗證』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
country_code |
string |
國家代碼 |
必填 |
store_uid |
string |
特約商店代碼 |
必填 |
method_type |
string |
驗證方式 |
必填 『驗證類型』值參考 |
user_id |
string |
消費者帳號,不區分大小寫,例如l12和L12是一樣的 |
必填 |
user_sn_type |
string |
證號類型 |
『證號類型』值參考 |
user_sn |
string |
付款人身分證/統一證號/護照號碼 |
|
user_phone |
string |
消費者家用電話 |
|
user_cellphone |
string |
消費者行動電話 |
|
user_email |
string |
消費者 E-Mail |
驗證方式為1,測試區請填(模擬簡訊) |
bank_code |
string |
銀行代碼 |
|
tix |
string |
交易代碼 |
驗證方式為2或4必填 『EDDA交易代碼』值參考 |
ip |
string |
消費者來源 IP |
必填 |
success_returl |
string |
驗證成功導頁 |
|
failure_returl |
string |
驗證失敗導頁 |
|
homepage_url |
string |
首頁網址 |
使用者於介面點選首頁按鈕即導回該頁面 |
『實名驗證』回傳欄位
參數名稱 |
型態 |
說明 |
必須 |
code |
string |
回傳碼 |
『電子錢包執行狀態碼』值參考 |
msg |
string |
回傳訊息 |
|
url |
string |
錢包開啟網址 |
|
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
值 |
型態 |
說明 |
備註 |
530 |
string |
慈善捐款 |
|
560 |
string |
購物(非電子支付機構) |
|
803 |
string |
消費貸款 |
|
902 |
string |
分期款 |
|
903 |
string |
保全費 |
|
904 |
string |
貨款 |
|
908 |
string |
現金加值 |
|
909 |
string |
租金(營利) |
|
910 |
string |
會費(營利) |
|
912 |
string |
仲介服務 |
|
忘記交易密碼
const crypto = require('crypto');
const httpRequest = require('https');
const { v4: uuidv4 } = require("uuid");
/**
* 經銷商串接-忘記交易密碼
*/
function AgentEWallet() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWallet.prototype.getRawData = function () {
return {
user_id: "userid"
};
};
/**
* 取得服務位置
*/
AgentEWallet.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/forgottp"
};
};
/**
* AES 256 加密
*/
AgentEWallet.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 到主機
*/
AgentEWallet.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWallet.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWallet.prototype.run = async function () {
json = await this.post(this.getPostData())
let file = require("fs");
file.writeFileSync("./tmp.json", json);
json = require("./tmp.json");
json.url = json.url.replace(/\\/g, "");
console.log(json);
};
/* 設定參數 */
AgentEWallet.prototype.setConfig = function (config) {
let {
agentUid,
agentKey,
storeUid
} = config;
this.agentUid = agentUid;
this.agentKey = agentKey;
this.storeUid = storeUid;
};
/* 忘記交易密碼 */
AgentEWallet.prototype.ewallet = async function (raw_data) {
let {
user_id,
type,
return_url
} = raw_data;
let res = await this.post({
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt({
user_id: user_id
}, this.agentKey)
});
let file = require("fs");
let tmp_name = uuidv4() + ".json";
file.writeFileSync(`/tmp/${tmp_name}`, res);
res = require(`/tmp/${tmp_name}`);
res.url = res.url.replace(/\\/g, "");
return res;
}
exports.ewallet_agent = new AgentEWallet();
if (process.argv[1].includes("forgot_tp_agent")) {
AgentEWallet = new AgentEWallet();
AgentEWallet.run();
}
經銷商『忘記交易密碼』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/forgottp"} JSON格式,AES256加密資料 |
encry_data |
text |
『忘記交易密碼』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
user_id |
string |
經銷商/特店會員編號,不區分大小寫,例如l12和L12是一樣的 |
必須 |
homepage_url |
string |
返回首頁連結 |
必須 |
『忘記交易密碼』回傳欄位
參數名稱 |
型態 |
說明 |
code |
string |
回傳碼(電子錢包執行狀態碼) |
msg |
string |
回傳訊息 |
url |
string |
錢包開啟網址 |
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
B600 |
string |
此會員沒設過交易密碼,不需執行忘記交易密碼流程 |
|
查詢會員電子錢包狀況
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 經銷商串接-查詢會員電子錢包狀況
*/
function AgentEWalletMemberStatus() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "tRYeF9wKZ4bKwzZoZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWalletMemberStatus.prototype.getRawData = function () {
return {
user_id: "user_id",
};
};
/**
* 取得服務位置
*/
AgentEWalletMemberStatus.prototype.getService = function () {
return {
service_name: "ocpap",
cmd: "api/memberstatus"
};
};
/**
* AES 256 加密
*/
AgentEWalletMemberStatus.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 到主機
*/
AgentEWalletMemberStatus.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWalletMemberStatus.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWalletMemberStatus.prototype.run = async function () {
json = await this.post(this.getPostData())
let file = require("fs");
console.log(json);
};
AgentEWalletMemberStatus = new AgentEWalletMemberStatus();
AgentEWalletMemberStatus.run();
經銷商『查詢會員電子錢包狀況』參數說明
欄位 |
型態 |
說明 |
agent_uid |
string(16) |
經銷商商務代號 |
service |
text |
{"service_name": "ocpap", "cmd": "api\/memberstatus"} JSON格式,AES256加密資料 |
encry_data |
text |
『查詢會員電子錢包狀況』欄位參考 JSON格式,AES256加密資料 |
參數名稱 |
型態 |
說明 |
必須 |
user_id |
string |
經銷商/特店會員編號,不區分大小寫,例如l12和L12是一樣的 |
必須 |
『查詢會員電子錢包狀況』回傳欄位
參數名稱 |
型態 |
說明 |
code |
string |
回傳碼(電子錢包執行狀態碼) |
msg |
string |
回傳訊息 |
status |
boolen |
true 代表有綁過卡 , false 代表沒綁過卡 |
值 |
型態 |
說明 |
備註 |
100 |
string |
資料不正確 |
|
400 |
string |
系統錯誤 |
|
B200 |
string |
執行成功 |
|
B500 |
string |
執行失敗 |
|
共用參數
參數名稱 |
型態 |
說明 |
必須 |
id |
string |
商品編號 |
必填 |
name |
string |
商品名稱 |
必填 |
cost |
string |
商品單價 |
必填 |
amount |
string |
商品數量 |
必填 |
total |
string |
商品小計 |
必填 |
值 |
型態 |
說明 |
備註 |
1 |
integer |
身份證字號(預設) |
|
2 |
integer |
統一證號 |
|
3 |
integer |
護照號碼 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
不開立電子發票 |
|
1 |
integer |
開立電子發票 |
|
2 |
integer |
依系統設定(預設) |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
應稅(預設) |
|
2 |
integer |
零稅率 |
|
3 |
integer |
免稅 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
未使用電子發票開立 |
|
1 |
integer |
雲端發票 |
|
2 |
integer |
發票捐贈 |
|
3 |
integer |
實體發票 |
|
值 |
型態 |
說明 |
備註 |
0 |
integer |
未使用雲端發票類型 |
|
2 |
integer |
手機條碼 |
|
3 |
integer |
自然人憑證條碼 |
|
4 |
integer |
以E-Mail寄送 |
|
值 |
型態 |
說明 |
備註 |
1 |
integer |
簡訊OTP驗證 |
|
2 |
integer |
eDDA銀行帳號驗證 |
|
3 |
integer |
TWID驗證 |
|
4 |
integer |
eDDA+TWID驗證 |
(擇一) |
5 |
integer |
國民身分證驗證 |
|
附錄一:測試區測試用信用卡卡號
下面卡號僅限在測試區測試使用
中國信託信用卡 (使用電子錢包功能適用)
|
Visa |
一般交易 |
4003618704777729 |
有效日期 |
任意 |
安全碼 |
任意 |
3D交易密碼 |
1234567 |
附錄二:資料加密方式說明
- 所有的API送出HTTPs請求之欄位中,service 和 encry_data 欄位皆進行 AES256+BASE64 加密處理。
- AES加密,格式為CBC,長度為256bits,金鑰長度32,IV長度16,傳遞內文為加密後組合IV並經過Base64轉換後傳出。
- IV資料建議隨機產生。
PHP加密示意:
AesEncrypt -> base64_ecode($IV . $JSON)
C#加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Java加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Node.js加密示意:
AesEncrypt -> concat([IV,JSON], [IV_SIZE,JSON_SIZE]) -> toString('base64')
Python加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> base64.b64encode
附錄三:API模擬串接服務