修改歷程
版本 | 異動日期 | 修訂內容 | 位置 |
---|---|---|---|
1.0 | 2023.04.20 | 初版 | |
1.1 | 2023.05.23 | 增加系統回報 | 20230523_01 |
1.2 | 2023.05.26 | 增加訂單查詢 | 20230526_01 |
1.3 | 2023.07.10 | 1.修正文件錯誤,service內的cmd應該是全小寫,誤植到大寫 2.增加直接建立請款單api |
誤植到大寫_01 誤植到大寫_02 直接建立請款單api |
1.4 | 2023.08.07 | 增加請款單延期 | 20230807_01 |
1.5 | 2023.09.18 | 1.請款單增加自訂參數 2.請款單回應調整回應格式 3.查詢請款單請求參數調整,回應格式調整 4.直接建立請款單增加自訂參數 5.直接建立請款單回應調整回應格式 6.請款單延期調整請求參數 |
20230918_01 20230918_02 20230918_03 20230918_04 20230918_05 20230918_06 |
開發前請先閱讀
金鑰的注意事項
Q:金鑰的有效期限
A:MyPay的金鑰有效期限只有一年,如果要延長,請自行至金鑰管理系統延長
Q:金鑰可以在什麼時候延長
A:到期日7天前,都可以延長(不包含第7天),一但超過,系統自動產生新金鑰後,即不可再延長
Q:金鑰即將到期前7天時,系統新發的金鑰是否會於信件中夾帶
A:會
Q:金鑰在到期前何時會發信通知
A:定期發送金鑰到期通知的頻率為:60天/30天/20天,若 貴司皆無動作,則系統會於到期前七天產製新金鑰並發送email到 貴司技術窗口信箱,此時舊的金鑰,因還未過期還可以使用,一旦過期,貴司仍使用舊金鑰則會無法交易
請款單結果的告知
Q:何時會給予請款單結果
A:請款單將在接收請求建立、付款完成、過期等狀態後,會由附錄三這邊的設定,背景主動發動通知商家
Q:當我接到請款單通知時該做什麼
A:請直接回應8888,如沒有回應,MyPay會通知5次,若5次內均得不到8888,系統會寄發信件給予商家的技術人員以及附錄三而外設定的人員
Q:請款單背景通知相關欄位
A:請查看請款單背景通知
支付工具注意事項
Q:目前支援的支付工具為何
A:目前僅支援信用卡及虛擬帳號
Q:虛擬帳號是否有可能造成重複付款或金額不符
A:代收的部分,不會造成此問題,如果非代收則要看店家與銀行端的申請
請款系統設計概要
安全性設計
所有的請求發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES加密,再透過HTTPS加密傳輸。
資料驗證
交易參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。
系統架構
所有的請求與查詢只能透過伺服器發動,由特約商店的網頁伺服器利用伺服端服務程式發動請求,以避免請求資料被竄改或攔截。為確保安全,請款單系統僅能接受 https connection,注意僅接受TLS1.2以上協定。
請款系統目前提供之服務與格式
請款系統提供應用方式,應用情境如下:
(1)請求建立請款單網址:發動端無須自建請款畫面,由Mypay提供畫面,當發動後,會給予請款單頁面網址。
(2)請求查詢請款單網址:查詢請款單。
(3)請求直接建立請款單網址:無需進到Mypay提供的畫面設定請款資料,直接產生請款單。
(4)請求請款單請款時間延長:請款時間已過期,不想成立新單,可呼叫此功能,將原本的請款單,請款時間延長,需注意,如使用虛擬帳號,則舊的匯款帳號將會失效,會重新產生一組新的
(5)請款單背景通知:系統接收到請款單建立或請款單狀態變更時,會透過在系統後台的通知網址,進行通知
我們提供介接方式是透過https連線,只接受POST方式傳送請求資料。
介接網址
特約商店模式
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/init |
正式區 | https://ka.mypay.tw/api/init |
資料加密方式
AES 256編碼 + base64編碼(附錄四資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
請求建立請款單
<?php
final class StoreCompanyInvoiceCreator
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/companyInvoiceCreator'
);
}
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['store_order_id'] = time();
return $rawData;
}
}
$StoreCompanyInvoiceCreator = new StoreCompanyInvoiceCreator();
$StoreCompanyInvoiceCreator->run();
?>
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-請款單建立
*/
function StoreCompanyInvoiceCreate() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCompanyInvoiceCreate.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
store_order_id: 1685000506,
};
};
/**
* 取得服務位置
*/
StoreCompanyInvoiceCreate.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/companyInvoiceCreator"
};
};
/**
* AES 256 加密
*/
StoreCompanyInvoiceCreate.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 到主機
*/
StoreCompanyInvoiceCreate.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCompanyInvoiceCreate.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCompanyInvoiceCreate.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCompanyInvoiceCreate = new StoreCompanyInvoiceCreate();
StoreCompanyInvoiceCreate.run();
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 StoreCompanyInvoiceCreate {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCompanyInvoiceCreate simulator = new StoreCompanyInvoiceCreate();
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("store_order_id", 1685000506);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/companyInvoiceCreator");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
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 StoreCompanyInvoiceCreate {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreCompanyInvoiceCreate simulator = new StoreCompanyInvoiceCreate();
//僅限走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.store_order_id = 1685000506;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/companyInvoiceCreator";
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; }
}
}
# -*- 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 StoreCompanyInvoiceCreate:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'store_order_id': 1685000506,
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/companyInvoiceCreator'
}
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)
StoreCompanyInvoiceCreate = StoreCompanyInvoiceCreate()
StoreCompanyInvoiceCreate.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"resultData": {
"code": "A0000",
"msg": "成功",
"store_order_id": "1695018535",
"order_uid": "f8f315d97d",
"key": "4703cc1c17a2d501fe319c7f3728b779",
"url": "https:\/\/pay.usecase.cc\/companyinvoice\/creator\/f8f315d97d.html",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
}
發動請求後,得到URL,倒轉到該URL頁面讓操作者確認或修改該請款單是否正確。
特約商店『建立請款單』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/companyinvoicecreator"} JSON格式,AES256加密資料 |
encry_data | text | 『建立請款單』欄位參考 JSON格式,AES256加密資料 |
『建立請款單』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string (16) | 請款方商務代號 | 必填 |
buy_store_uid | string (16) | 購買方商務代號(不一定有) | 非必填 |
store_order_id | string | 客戶自訂請款單號,不可重複 | 必填 |
tong_bian | string(10) | 賣方統編 | 非必填 |
company_name | string(20) | 賣方公司名稱 | 非必填 |
company_trl | string(20) | 賣方公司電話 | 非必填 |
sales_name | string(20) | 賣方負責業務姓名 | 非必填 |
sales_phone | string(10) | 賣方負責業務手機 | 非必填 |
sales_tel | string(12) | 賣方負責業務電話 | 非必填 |
sales_mail | string(50) | 賣方負責業務email | 非必填 |
buy_company_tong_bian | string(10) | 買方統編 | 非必填 |
buy_company_name | string(20) | 買方公司名稱 | 非必填 |
buy_company_tel | string(20) | 買方公司電話 | 非必填 |
buy_name | string(20) | 買方業務姓名 | 非必填 |
buy_phone | string(10) | 買方業務手機 | 非必填 |
buy_tel | string(10) | 買方業務電話 | 非必填 |
buy_mail | string(50) | 買方業務mail | 非必填 |
date_start | string | 請款起始日 格式:YYYY-MM-DD |
非必填 |
date_end | string | 請款截止日 格式:YYYY-MM-DD |
非必填 |
create_type | integer(2) | 發票先開後開,預設 0 | 非必填 『發票開立先後順序』設定值 |
product_tax_type | integer(2) | 商品價格是否含稅,預設 1 | 非必填 『資料含不含稅』設定值 |
tax_type | integer(2) | 營業稅,預設 1 | 非必填 『發票稅率別』設定值 |
tax | string(5) | 稅額 | 非必填 |
pay_mode | array | 付款方式 | 非必填 使用前請先確認與MyPay的簽約是否有該項支付工具 『付款方式』設定值 |
product_detail | array | 請款項目 | 非必填 『請款項目資料』欄位 |
discount_type | integer(2) | 折扣方式,預設0 | 非必填 『折扣類型』設定值 |
discount | integer(7) | 折扣方式,預設0 | 非必填 不可有小數,如果是減價0以上都可,如果是折扣0~100 |
memo | string(255) | 備註 | 非必填 |
echo_0 | string(500) | 自訂回傳參數 1 | 非必填 |
echo_1 | string(500) | 自訂回傳參數 2 | 非必填 |
echo_2 | string(500) | 自訂回傳參數 3 | 非必填 |
echo_3 | string(500) | 自訂回傳參數 4 | 非必填 |
echo_4 | string(500) | 自訂回傳參數 5 | 非必填 |
『請款單建立』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 請求回傳碼 | 『執行狀態碼』值參考 |
msg | string | 代碼訊息 | |
resultData | object | 執行結果 | 『請款單建立結果』欄位參考 |
『請款單建立結果』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 執行結果回傳代碼 | 『請款單建立回應代碼』值參考 |
msg | string | 執行結果錯誤訊息 | |
store_order_id | string | 商店訂單編號 | |
order_uid | string | 回傳請款單系統提供單號 | |
key | string | 驗證碼 | |
url | string | 請款單輸入介面網址 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『請款單建立回應代碼』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A0000 | string | 成功 | |
A0001 | string | 客戶自訂請款單號重複 | |
A0002 | string | 失敗 | |
A0003 | string | 發票備註超過70個字 |
查詢請款單
發動請求後,取得請款單的目前資訊。
<?php
/**
* 特約商店串接-查詢請款單
*/
final class StoreCompanyInvoiceInquire
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/companyInvoiceInquiry'
);
}
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['page_code'] = "1cf169f897";
$rawData['store_order_id'] = "2023041800001";
return $rawData;
}
}
$StoreCompanyInvoiceInquire = new StoreCompanyInvoiceInquire();
$StoreCompanyInvoiceInquire->run();
?>
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-查詢請款單
*/
function StoreCompanyInvoiceInquire() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCompanyInvoiceInquire.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
page_code: "1cf169f897",
store_order_id: "2023041800001",
};
};
/**
* 取得服務位置
*/
StoreCompanyInvoiceInquire.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/companyInvoiceInquiry"
};
};
/**
* AES 256 加密
*/
StoreCompanyInvoiceInquire.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 到主機
*/
StoreCompanyInvoiceInquire.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCompanyInvoiceInquire.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCompanyInvoiceInquire.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCompanyInvoiceInquire = new StoreCompanyInvoiceInquire();
StoreCompanyInvoiceInquire.run();
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 StoreCompanyInvoiceInquire {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCompanyInvoiceInquire simulator = new StoreCompanyInvoiceInquire();
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("page_code", "1cf169f897");
rawData.put("store_order_id", "2023041800001");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/companyInvoiceInquiry");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
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 StoreCompanyInvoiceInquire {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreCompanyInvoiceInquire simulator = new StoreCompanyInvoiceInquire();
//僅限走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.page_code = "1cf169f897";
rawData.store_order_id = "2023041800001";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/companyInvoiceInquiry";
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; }
}
}
# -*- 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 StoreCompanyInvoiceInquire:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'page_code': "1cf169f897",
'store_order_id': "2023041800001",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/companyInvoiceInquiry'
}
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)
StoreCompanyInvoiceInquire = StoreCompanyInvoiceInquire()
StoreCompanyInvoiceInquire.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"resultData": {
"code": "A0000",
"msg": "成功",
"page_code": "87950ffc60",
"store_uid": "289151880026",
"key": "c9715ac2bcb631e392252b0992f84dae",
"buy_store_uid": "",
"state": "3",
"store_order_id": "1695008080",
"tong_bian": "28915188",
"company_name": "高鉅科技股份有限公司",
"company_tel": "04-2322-0267",
"sales_name": "李逍遙",
"sales_tel": "04-2322-0267#11",
"sales_phone": "0911111111",
"sales_mail": "[email protected]",
"buy_company_tong_bian": "04541302",
"buy_company_name": "FG",
"buy_company_tel": "01-11111111",
"buy_name": "趙靈兒",
"buy_phone": "0922222222",
"buy_tel": "",
"buy_mail": "[email protected]",
"date_start": "2023-09-18",
"date_end": "2023-10-18",
"create_type": "1",
"product_tax_type": "1",
"tax_type": "1",
"tax": "0.05",
"pay_mode": [
"CREDITCARD"
],
"final_pay_mode": "CREDITCARD",
"product_detail": [
{
"name": "螺絲",
"price": "0.1",
"selling_price": "0.1",
"quantity": "12"
},
{
"name": "螺絲2",
"price": "1.1",
"selling_price": "1.1",
"quantity": "12"
}
],
"discount_type": "0",
"discount": "0",
"total_discount_amount": "0",
"sales_amount": "13",
"tax_amount": "1",
"total_amount": "14",
"memo": "0",
"invoice_date": "2023-09-18 11:36:45",
"invoice_number": "TG00000073",
"invoice_detail": [
{
"Description": "螺絲",
"Quantity": "12",
"UnitPrice": "0.1",
"Amount": 1.2000000000000002
},
{
"Description": "螺絲2",
"Quantity": "12",
"UnitPrice": "1.1",
"Amount": 13.200000000000001
},
{
"description": "螺絲",
"quantity": 12,
"unit_price": 0,
"amount": 1
},
{
"description": "螺絲2",
"quantity": 12,
"unit_price": 1,
"amount": 13
}
],
"cash_order": [
{
"cash_uid": "148498",
"pay_date": "2023-09-18 11:37:13",
"prc": "250"
}
],
"invoice_memo": "",
"echo0": "",
"echo1": "",
"echo2": "",
"echo3": "",
"echo4": ""
}
}
特約商店『查詢請款單』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/companyinvoiceinquiry"} JSON格式,AES256加密資料 |
encry_data | text | 『查詢請款單』欄位參考 JSON格式,AES256加密資料 |
『查詢請款單』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string (16) | 請款方商務代號 | 必填 |
store_order_id | string(30) | 客戶自訂請款單號 | page_code或store_order_id擇一必填 |
page_code | string | 請款系統代碼 | page_code或store_order_id擇一必填 |
『查詢請款單』回傳欄位
參數名稱 | 型態 | 說明 | 備註 |
---|---|---|---|
code | string | 『資料接收回傳代碼』對照表 | |
msg | string | 資料接收回傳代碼 | |
resultData | object | 『查詢結果』 |
『查詢請款單』回傳欄位
參數名稱 | 型態 | 說明 | 備註 |
---|---|---|---|
code | string | 『查詢結果代碼』對照表 | |
msg | string | 查詢結果訊息 | |
page_code | string | 請款系統代碼 | |
store_uid | string | 請款方商務代號 | |
buy_store_uid | string | 購買方商務代號(不一定有) | |
state | string | 『訂單狀態』設定值 | |
store_order_id | string | 客戶自訂請款單號 | |
tong_bian | string | 賣方統編 | |
company_name | string | 賣方公司名稱 | |
company_tel | string | 賣方公司電話 | |
sales_name | string | 賣方負責業務姓名 | |
sales_tel | string | 賣方負責業務電話 | |
sales_phone | string | 賣方負責業務手機 | |
sales_mail | string | 賣方負責業務mail | |
buy_company_tong_bian | string | 買方公司統編 | |
buy_company_name | string | 買方公司名稱 | |
buy_company_tel | string | 買方公司電話 | |
buy_name | string | 買方業務姓名 | |
buy_phone | string | 買方業務手機 | |
buy_tel | string | 買方業務電話 | |
buy_mail | string | 購買人mail | |
date_start | string | 請款起始日 | |
date_end | string | 請款截止日 | |
create_type | string | 『發票開立先後順序』設定值 | |
product_tax_type | string | 『資料含不含稅』設定值 | |
tax_type | string | 『稅率類型』設定值 | |
tax | string | 稅額 | |
pay_mode | array | 『付款方式』設定值 | |
final_pay_mode | string | 消費者決定使用的支付工具 | |
product_detail | array | 『請款項目資料』欄位 | |
discount_type | string | 『折扣類型』設定值 | |
discount | string | 依據折扣方式代表不同的意義如果是打折不得大於100 | |
total_discount_amount | string | 折扣總額 | |
sales_amount | string | 銷售額合計 | |
tax_amount | string | 營業稅額 | |
total_amount | string | 請款總金額 | |
memo | string | 備註 | |
invoice_date | string | 發票日期 | |
invoice_number | string | 發票號碼 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 | |
cash_order | array | 『金流回應相關值』設定值 |
『查詢結果對應代碼』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A0000 | 查詢成功 | ||
A0001 | 查無資料 | ||
A0002 | 請款系統代碼或客戶自訂請款單號,兩者需有一個有值 |
直接建立請款單
<?php
/**
* 特約商店串接-請款單直接建立建立
*/
final class StoreCompanyInvoiceDirectCreator
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['store_order_id'] = 1688967861;
$rawData['mode'] = 2;
$rawData['tong_bian'] = "28915188";
$rawData['company_name'] = "高鉅科技股份有限公司";
$rawData['company_tel'] = "04-2322-0267";
$rawData['sales_name'] = "李逍遙";
$rawData['sales_phone'] = "0911111111";
$rawData['sales_tel'] = "04-2322-0267#11";
$rawData['sales_mail'] = "[email protected]";
$rawData['buy_name'] = "趙靈兒";
$rawData['buy_phone'] = "0922222222";
$rawData['buy_mail'] = "[email protected]";
$rawData['date_start'] = "2023-07-10";
$rawData['date_end'] = "2023-08-10";
$rawData['create_type'] = 3;
$rawData['product_tax_type'] = 1;
$rawData['tax_type'] = 1;
$rawData['tax'] = "0.05";
$rawData['pay_mode'] = [
"CREDITCARD"
];
$rawData['product_detail'] = [
[
'name' => 'iphone 14 PRO',
'price' => '30000',
'selling_price' => '30000',
'quantity' => '3'
]
];
$rawData['discount_type'] = 0;
$rawData['discount'] = 0;
$rawData['memo'] = 0;
$rawData['send_mail_type'] = 1;
$rawData['mail_subject'] = "請款信件主題";
$rawData['mail_content'] = "請款信件內容";
$rawData['mail_users'] = [
"[email protected]"
];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/companyinvoicedirectcreator'
);
}
/**
* 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, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreCompanyInvoiceDirectCreator = new StoreCompanyInvoiceDirectCreator();
$StoreCompanyInvoiceDirectCreator->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 StoreCompanyInvoiceDirectCreator {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreCompanyInvoiceDirectCreator simulator = new StoreCompanyInvoiceDirectCreator();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
ArrayList payMode = new ArrayList();
payMode.Add("CREDITCARD");
ArrayList productDetail = new ArrayList();
dynamic productDetail1 = new ExpandoObject();
productDetail1.name = "iphone 14 PRO";
productDetail1.price = "30000";
productDetail1.selling_price = "30000";
productDetail1.quantity = "3";
productDetail.Add(productDetail1);
ArrayList mailUsers = new ArrayList();
mailUsers.Add("[email protected]");
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.store_order_id = 1688967861;
rawData.mode = 2;
rawData.tong_bian = "28915188";
rawData.company_name = "高鉅科技股份有限公司";
rawData.company_tel = "04-2322-0267";
rawData.sales_name = "李逍遙";
rawData.sales_phone = "0911111111";
rawData.sales_tel = "04-2322-0267#11";
rawData.sales_mail = "[email protected]";
rawData.buy_name = "趙靈兒";
rawData.buy_phone = "0922222222";
rawData.buy_mail = "[email protected]";
rawData.date_start = "2023-07-10";
rawData.date_end = "2023-08-10";
rawData.create_type = 3;
rawData.product_tax_type = 1;
rawData.tax_type = 1;
rawData.tax = "0.05";
rawData.pay_mode = payMode;
rawData.product_detail = productDetail;
rawData.discount_type = 0;
rawData.discount = 0;
rawData.memo = 0;
rawData.send_mail_type = 1;
rawData.mail_subject = "請款信件主題";
rawData.mail_content = "請款信件內容";
rawData.mail_users = mailUsers;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/companyinvoicedirectcreator";
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; }
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-請款單直接建立建立
*/
function StoreCompanyInvoiceDirectCreator() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCompanyInvoiceDirectCreator.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
store_order_id: 1688967861,
mode: 2,
tong_bian: "28915188",
company_name: "高鉅科技股份有限公司",
company_tel: "04-2322-0267",
sales_name: "李逍遙",
sales_phone: "0911111111",
sales_tel: "04-2322-0267#11",
sales_mail: "[email protected]",
buy_name: "趙靈兒",
buy_phone: "0922222222",
buy_mail: "[email protected]",
date_start: "2023-07-10",
date_end: "2023-08-10",
create_type: 3,
product_tax_type: 1,
tax_type: 1,
tax: "0.05",
pay_mode: [
"CREDITCARD"
],
product_detail: [
{
'name': "iphone 14 PRO",
'price': "30000",
'selling_price': "30000",
'quantity': "3"
}
],
discount_type: 0,
discount: 0,
memo: 0,
send_mail_type: 1,
mail_subject: "請款信件主題",
mail_content: "請款信件內容",
mail_users: [
"[email protected]"
],
};
};
/**
* 取得服務位置
*/
StoreCompanyInvoiceDirectCreator.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/companyinvoicedirectcreator"
};
};
/**
* AES 256 加密
*/
StoreCompanyInvoiceDirectCreator.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 到主機
*/
StoreCompanyInvoiceDirectCreator.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCompanyInvoiceDirectCreator.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCompanyInvoiceDirectCreator.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCompanyInvoiceDirectCreator = new StoreCompanyInvoiceDirectCreator();
StoreCompanyInvoiceDirectCreator.run();
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 StoreCompanyInvoiceDirectCreator {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCompanyInvoiceDirectCreator simulator = new StoreCompanyInvoiceDirectCreator();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
ArrayList payMode = new ArrayList();
payMode.add(new String("CREDITCARD"));
ArrayList productDetail = new ArrayList();
Map<Object, Object> productDetail1 = new HashMap<Object, Object>();
productDetail1.put("name", "iphone 14 PRO");
productDetail1.put("price", "30000");
productDetail1.put("selling_price", "30000");
productDetail1.put("quantity", "3");
productDetail.add(productDetail1);
ArrayList mailUsers = new ArrayList();
mailUsers.add(new String("[email protected]"));
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("store_order_id", 1688967861);
rawData.put("mode", 2);
rawData.put("tong_bian", "28915188");
rawData.put("company_name", "高鉅科技股份有限公司");
rawData.put("company_tel", "04-2322-0267");
rawData.put("sales_name", "李逍遙");
rawData.put("sales_phone", "0911111111");
rawData.put("sales_tel", "04-2322-0267#11");
rawData.put("sales_mail", "[email protected]");
rawData.put("buy_name", "趙靈兒");
rawData.put("buy_phone", "0922222222");
rawData.put("buy_mail", "[email protected]");
rawData.put("date_start", "2023-07-10");
rawData.put("date_end", "2023-08-10");
rawData.put("create_type", 3);
rawData.put("product_tax_type", 1);
rawData.put("tax_type", 1);
rawData.put("tax", "0.05");
rawData.put("pay_mode", payMode);
rawData.put("product_detail", productDetail);
rawData.put("discount_type", 0);
rawData.put("discount", 0);
rawData.put("memo", 0);
rawData.put("send_mail_type", 1);
rawData.put("mail_subject", "請款信件主題");
rawData.put("mail_content", "請款信件內容");
rawData.put("mail_users", mailUsers);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/companyinvoicedirectcreator");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
# -*- 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 StoreCompanyInvoiceDirectCreator:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'store_order_id': 1688967861,
'mode': 2,
'tong_bian': "28915188",
'company_name': "高鉅科技股份有限公司",
'company_tel': "04-2322-0267",
'sales_name': "李逍遙",
'sales_phone': "0911111111",
'sales_tel': "04-2322-0267#11",
'sales_mail': "[email protected]",
'buy_name': "趙靈兒",
'buy_phone': "0922222222",
'buy_mail': "[email protected]",
'date_start': "2023-07-10",
'date_end': "2023-08-10",
'create_type': 3,
'product_tax_type': 1,
'tax_type': 1,
'tax': "0.05",
'pay_mode': [
"CREDITCARD"
],
'product_detail': [
{
'name': "iphone 14 PRO",
'price': "30000",
'selling_price': "30000",
'quantity': "3"
}
],
'discount_type': 0,
'discount': 0,
'memo': 0,
'send_mail_type': 1,
'mail_subject': "請款信件主題",
'mail_content': "請款信件內容",
'mail_users': [
"[email protected]"
],
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/companyinvoicedirectcreator'
}
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)
StoreCompanyInvoiceDirectCreator = StoreCompanyInvoiceDirectCreator()
StoreCompanyInvoiceDirectCreator.run()
回傳 JSON 結構如下:
{
"code":"B200",
"msg":"執行成功",
"resultData":{
"code":"A0000",
"msg":"成功",
"store_order_id":"1695104732",
"order_uid":"bead516bc3",
"key":"995751385a82e4552b56b0c5f7106410",
"url":"https://pay.usecase.cc/companyinvoice/pay/bead516bc3.html",
"echo_0":"",
"echo_1":"",
"echo_2":"",
"echo_3":"",
"echo_4":""
}
}
特約商店『直接建立請款單』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/companyinvoicedirectcreator"} JSON格式,AES256加密資料 |
encry_data | text | 『直接建立請款單』欄位參考 JSON格式,AES256加密資料 |
『直接建立請款單』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 請款方商務代號 | 必填 |
buy_store_uid | string | 購買方商務代號(不一定有) | 非必填 |
mode | integer(1) | 請款對象 | 必填 『請款對象』設定值 |
store_order_id | string(30) | 客戶自訂請款單號 | 必填 |
tong_bian | string(8) | 賣方統編 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
company_name | string(20) | 賣方公司名稱 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
company_tel | string(20) | 賣方公司電話 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
sales_name | string(20) | 賣方負責業務姓名 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
sales_phone | string(10) | 賣方負責業務手機 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
sales_tel | string(12) | 賣方負責業務電話 | 非必填,如未填,則以在mypay申請記錄的資料為主 |
sales_mail | string(50) | 賣方負責業務email | 非必填,如未填,則以在mypay申請記錄的資料為主 |
buy_company_tong_bian | string(10) | 買方統編 | 當請款對象為1時必填 |
buy_company_name | string(20) | 買方公司名稱 | 當請款對象為1時必填 |
buy_company_tel | string(20) | 買方公司電話 | 當請款對象為1時必填 |
buy_name | string(20) | 買方業務姓名 | 必填 |
buy_phone | string(10) | 買方業務手機 | 必填 |
buy_tel | string(10) | 買方業務電話 | 當請款對象為1時必填 |
buy_mail | string(50) | 買方業務mail | 必填 |
date_start | string | 請款起始日 格式:YYYY-MM-DD |
必填 |
date_end | string | 請款截止日 格式:YYYY-MM-DD |
必填,截止日不得小於發送API當日 |
create_type | integer(2) | 收據類型開立模式 | 必填 『收據類型開立模式』,設定值 |
product_tax_type | integer(2) | 商品價格是否含稅,預設 1 | 收據類型開立模式為1或2時為必填 『資料含不含稅』設定值 |
tax_type | integer(2) | 營業稅 | 收據類型開立模式為1或2時為必填 『發票稅率別』設定值 |
tax | string(5) | 稅額 | 收據類型開立模式為1或2時且發票稅率別為必填 |
pay_mode | array | 付款方式 | 必填 使用前請先確認與MyPay的簽約是否有該項支付工具 『付款方式』設定值 |
product_detail | array | 請款項目 | 必填 『請款項目資料』欄位 |
discount_type | integer(2) | 折扣方式,預設0 | 非必填 『折扣類型』設定值 |
discount | integer(7) | 折扣方式,預設0 | 非必填 不可有小數,如果是減價0以上都可,如果是折扣0~100 |
memo | string(255) | 備註 | 非必填 |
echo_0 | string(500) | 自訂回傳參數 1 | 非必填 |
echo_1 | string(500) | 自訂回傳參數 2 | 非必填 |
echo_2 | string(500) | 自訂回傳參數 3 | 非必填 |
echo_3 | string(500) | 自訂回傳參數 4 | 非必填 |
echo_4 | string(500) | 自訂回傳參數 5 | 非必填 |
send_mail_type | integer(1) | 是否需要寄信,預設1 | 必填 『是否需要寄信』設定值 |
mail_subject | string(255) | 信件主旨 | 必填 |
mail_content | string(255) | 信件內容 | 必填 |
mail_users | array(255) | 收信者email | 非必填,如未填會自動抓取買方mail |
『直接建立請款單』回傳欄位
參數名稱 | 型態 | 說明 | 備註 |
---|---|---|---|
code | string | 『資料接收回傳代碼』對照表 | |
msg | string | 資料接收回傳代碼 | |
resultData | object | 『直接建立請款單執行結果』 |
『直接建立請款單執行結果』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 執行結果回傳代碼 | 『直接建立請款單回傳代碼』值參考 |
msg | string | 執行結果錯誤訊息 | |
store_order_id | string | 商店訂單編號 | |
order_uid | string | 回傳請款單系統提供單號 | |
key | string | 驗證碼 | |
url | string | 請款單付款網址 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『直接建立請款單回傳代碼』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A0000 | string | 成功 | |
A0001 | string | 稅額不正確 | |
A0002 | string | 發票稅率別不正確 | |
A0003 | string | 請款單不存在 | |
A0004 | string | 可使用支付工具錯誤 | |
A0005 | string | 請款日期大於結束日期 | |
A0006 | string | 商品金額減去折扣,不等於總金額 | |
A0007 | string | 系統錯誤,請稍後在試 | |
A0008 | string | 信件主旨未填 | |
A0009 | string | 信件內容未填 | |
A0010 | string | 沒有收件者 | |
A0011 | string | 查無商家資料 | |
A0012 | string | 客戶自訂請款單號重複 | |
A0013 | string | 發票備註超過70個字 | |
A0014 | string | 發票開立失敗 | |
A0015 | string | 發票號碼不足 |
請款單請款時間延長
請款時間已過期,不想成立新單,可呼叫此功能,將原本的請款單,請款時間延長,需注意,如使用虛擬帳號,則舊的匯款帳號將會失效,會重新產生一組新的
<?php
/**
* 特約商店串接-請款單延長
*/
final class StoreCompanyInvoiceOrderExtension
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/companyinvoiceorderextension'
);
}
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['page_code'] = "e0dc6daa3c";
$rawData['date_start'] = "2023-08-01";
$rawData['date_end'] = "2023-10-01";
return $rawData;
}
}
$StoreCompanyInvoiceOrderExtension = new StoreCompanyInvoiceOrderExtension();
$StoreCompanyInvoiceOrderExtension->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 StoreCompanyInvoiceOrderExtension {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreCompanyInvoiceOrderExtension simulator = new StoreCompanyInvoiceOrderExtension();
//僅限走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.page_code = "e0dc6daa3c";
rawData.date_start = "2023-08-01";
rawData.date_end = "2023-10-01";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/companyinvoiceorderextension";
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 StoreCompanyInvoiceOrderExtension {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCompanyInvoiceOrderExtension simulator = new StoreCompanyInvoiceOrderExtension();
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("page_code", "e0dc6daa3c");
rawData.put("date_start", "2023-08-01");
rawData.put("date_end", "2023-10-01");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/companyinvoiceorderextension");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
System.out.println(e.getMessage());
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-請款單延長
*/
function StoreCompanyInvoiceOrderExtension() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCompanyInvoiceOrderExtension.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
page_code: "e0dc6daa3c",
date_start: "2023-08-01",
date_end: "2023-10-01",
};
};
/**
* 取得服務位置
*/
StoreCompanyInvoiceOrderExtension.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/companyinvoiceorderextension"
};
};
/**
* AES 256 加密
*/
StoreCompanyInvoiceOrderExtension.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 到主機
*/
StoreCompanyInvoiceOrderExtension.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCompanyInvoiceOrderExtension.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCompanyInvoiceOrderExtension.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCompanyInvoiceOrderExtension = new StoreCompanyInvoiceOrderExtension();
StoreCompanyInvoiceOrderExtension.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 StoreCompanyInvoiceOrderExtension:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'page_code': "e0dc6daa3c",
'date_start': "2023-08-01",
'date_end': "2023-10-01",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/companyinvoiceorderextension'
}
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)
StoreCompanyInvoiceOrderExtension = StoreCompanyInvoiceOrderExtension()
StoreCompanyInvoiceOrderExtension.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "1685072654"
}
特約商店『請款單延長』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name":"api","cmd":"api\/companyinvoiceorderextension"} JSON格式,AES256加密資料 |
encry_data | text | 『請款單延長』欄位參考 JSON格式,AES256加密資料 |
『請款單延長』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 請款方商務代號 | 必填 |
page_code | string | 請款系統請款單號 | 與store_order_id則一必填 |
store_order_id | string | 自訂請款單號 | 與page_code則一必填 |
date_start | string | 要延長的起始日,格式 YYYY-MM-DD | 必填 |
date_end | string | 要延長的結束日,格式 YYYY-MM-DD | 必填 |
『請款單延長』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 資料接收回傳代碼 | 『資料接收回傳代碼』值參考 |
msg | string | 查詢結果訊息 |
請款單背景通知
不論是從後台、API等方式建立請款單,或著請款單過期、付款完成,均會透過在後台設定的通知網址,進行通知。
『回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
page_code | string | 請款系統代碼 | |
store_uid | string | 請款方商務代號 | |
key | string | 驗證碼 | |
buy_store_uid | string | 購買方商務代號(不一定有) | |
state | string | 『訂單狀態』設定值 | |
store_order_id | string | 客戶自訂請款單號 | |
tong_bian | string | 賣方統編 | |
company_name | string | 賣方公司名稱 | |
company_tel | string | 賣方公司電話 | |
sales_name | string | 賣方負責業務姓名 | |
sales_tel | string | 賣方負責業務電話 | |
sales_phone | string | 賣方負責業務手機 | |
sales_mail | string | 賣方負責業務mail | |
buy_company_tong_bian | string | 買方公司統編 | |
buy_company_name | string | 買方公司名稱 | |
buy_company_tel | string | 買方公司電話 | |
buy_name | string | 買方業務姓名 | |
buy_phone | string | 買方業務手機 | |
buy_tel | string | 買方業務電話 | |
buy_mail | string | 購買人mail | |
date_start | string | 請款起始日 | |
date_end | string | 請款截止日 | |
create_type | string | 『發票開立先後順序』設定值 | |
product_tax_type | string | 『資料含不含稅』設定值 | |
tax_type | string | 『稅率類型』設定值 | |
tax | string | 稅額 | |
pay_mode | array | 『付款方式』設定值 | |
final_pay_mode | string | 消費者決定使用的支付工具 | |
product_detail | array | 『請款項目資料』欄位 | |
discount_type | string | 『折扣類型』設定值 | |
discount | string | 依據折扣方式代表不同的意義如果是打折不得大於100 | |
total_discount_amount | string | 折扣總額 | |
sales_amount | string | 銷售額合計 | |
tax_amount | string | 營業稅額 | |
total_amount | string | 請款總金額 | |
memo | string | 備註 | |
invoice_date | string | 發票日期 | |
invoice_number | string | 發票號碼 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 | |
cash_order | array | 『金流回應相關值』設定值 |
值 | 型態 | 說明 | 備註 |
---|---|---|---|
A0000 | string | 成功 | |
A0001 | string | 客戶自訂請款單號重複 | |
A0002 | string | 失敗 |
各項參數對應值
『訂單狀態』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | 建檔中 | |
2 | integer | 請款中 | |
3 | integer | 已付款 | |
4 | integer | 過期 |
『收據類型開類模式』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 不預設 | 預設 |
1 | integer | 發票先開 | |
2 | integer | 發票後開 | |
3 | integer | 免用統一發票先開 | |
4 | integer | 免用統一發票後開 |
『資料含不含稅』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 無稅務 | |
1 | integer | 含稅 | |
2 | integer | 不含稅 |
『發票稅率別』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 無稅 | |
1 | integer | 應稅 | |
2 | integer | 零稅率 | |
3 | integer | 零稅率 |
『付款方式』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
all | string | 全部可用支付方式(預設) | |
MobilePayAll | string | 指定下列線上行動支付方式 支付寶、Pi 拍錢包、LINEPay、微信、街口支付、悠遊付 |
|
CREDITCARD | string | 信用卡 | |
CSTORECODE | string | 超商代碼 | |
WEBATM | string | WEBATM | |
E_COLLECTION | string | 虛擬帳號 | |
UNIONPAY | string | 銀聯卡 | |
SVC | string | 點數卡(GASH ,Imoney) | |
ABROAD | string | 海外信用卡 | |
ALIPAY | string | 支付寶 | |
string | 微信支付 | ||
DIRECTDEBIT | string | 定期定額扣款 | |
LINEPAYON | string | LINE Pay線上付款 | |
LINEPAYOFF | string | LINE Pay線下付款 | |
WECHATOFF | string | 微信支付線下 | |
APPLEPAY | string | APPLE PAY | |
GOOGLEPAY | string | Google Pay | |
EACH | string | eACH交易 | |
C_INSTALLMENT | string | 信用卡分期 | |
C_REDEEM | string | 信用卡紅利 | |
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 | 超商條碼繳費 |
『請款項目資料』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
name | string | 請款名目 | 必填 |
price | string | 定價 | 非必填 |
selling_price | string | 售價 | 必填 |
quantity | string | 數量 | 必填 |
『折扣類型』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 無折扣 | 預設 |
1 | integer | 減價 | |
2 | integer | 打折 |
『請款對象』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
1 | integer | B2B(公司戶) | 預設 |
2 | integer | B2C(消費者) |
『是否需要寄信』欄位
值 | 型態 | 說明 | 備註 |
---|---|---|---|
0 | integer | 不需要 | |
1 | integer | 需要 | 預設 |
『金流回應相關值』值內容
參數名稱 | 型態 | 說明 |
---|---|---|
cash_uid | string | 金流系統訂單單號 |
prc | string | 金流交易回傳碼 |
pay_date | string | 付款完成時間,格式YYYY-mm-dd H:i:s |
『執行狀態碼』值內容
值 | 型態 | 說明 | 備註 |
---|---|---|---|
100 | string | 資料不正確 | |
400 | string | 系統錯誤 | |
B200 | string | 執行成功 | |
B500 | string | 執行失敗 |
附錄一:PFN(支付工具)參數表
資料傳遞可使用編號,也可以使用代碼 。 例如:pfn=27跟pfn=PION,一樣都是使用Pi 拍錢包線上付款支付工具。
編號 | 代碼 | 狀態 | 說明 |
---|---|---|---|
1 | CREDITCARD | 啟用 | 信用卡 |
3 | CSTORECODE | 啟用 | 超商代碼 |
4 | WEBATM | 啟用 | WEBATM |
6 | E_COLLECTION | 啟用 | 虛擬帳號 (ATM轉帳) |
9 | ABROAD | 啟用 | 海外信用卡(非台灣發行信用卡) |
10 | ALIPAY | 啟用 | 支付寶 |
13 | 啟用 | 微信支付 | |
14 | DIRECTDEBIT | 啟用 | 定期扣款(國內信用卡) |
15 | LINEPAYON | 啟用 | LINE線上付款(消費者主掃) |
16 | LINEPAYOFF | 啟用 | LINE線下付款(消費者被掃) |
19 | WECHATOFF | 啟用 | 微信支付線下 |
20 | APPLEPAY | 啟用 | Apple Pay |
21 | GOOGLEPAY | 啟用 | Google Pay |
23 | C_INSTALLMENT | 啟用 | 信用卡分期 |
24 | C_REDEEM | 啟用 | 信用卡紅利 |
25 | CARDLESS | 啟用 | 無卡分期(由資融公司提供分期服務) |
27 | PION | 啟用 | Pi 拍錢包線上付款(消費者主掃) |
28 | PIOFF | 啟用 | Pi 拍錢包線下付款(消費者被掃) |
29 | AMEX | 啟用 | 美國運通 |
31 | JKOON | 啟用 | 街口支付線上付款(消費者主掃) |
32 | JKOOF | 啟用 | 街口支付線下付款(消費者被掃) |
33 | ALIPAYOFF | 啟用 | 支付寶線下(消費者被掃) |
34 | M_RECHARGE | 啟用 | 儲值 |
36 | AFP | 啟用 | 後付款 |
37 | CASH | 啟用 | 現金 |
38 | EASYWALLETON | 啟用 | 悠遊付線上付款(消費者主掃) |
39 | EASYWALLETOFF | 啟用 | 悠遊付線下付款(消費者被掃) |
40 | EASYCARD | 啟用 | 悠遊卡 |
41 | IPASS | 啟用 | 一卡通 |
42 | ICASH | 啟用 | iCash |
43 | BARCODE | 啟用 | 超商條碼繳費 |
51 | DIRECTDEBIT_ABROAD | 啟用 | 定期扣款(國外信用卡) |
附錄二:交易狀態代碼
以下回傳的狀態代碼均須處理,避免系統連線錯誤,MYPAY LINK回傳時,貴司系統無法判讀
狀態代碼 | 狀態說明 | 詳細說明 |
---|---|---|
100 | 資料錯誤 | MYPAYLINK收到資料,但是格式或資料錯誤 |
200 | 資料正確 | MYPAYLINK收到正確資料,會接續下一步 |
B200 | 執行成功 | 處理成功執行 |
B500 | 執行失敗 | 處理時,資料異常不予以處理 |
附錄三:設定調整
- 請款單回傳設定,通知網址的部分,僅限80跟443 port
- 金鑰重新發送與變更
附錄四:資料加密方式說明
- 所有的API送出HTTPs請求之欄位中,service 和 encry_data 欄位皆進行 AES256+BASE64 加密處理。
- AES加密,格式為CBC,長度為256bits,金鑰長度32,IV長度16,傳遞內文為加密後組合IV並經過Base64轉換後傳出。
- IV資料建議隨機產生。
PHP加密示意:
AesEncrypt -> base64_ecode($IV . $JSON)
C#加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Java加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> toBase64
Node.js加密示意:
AesEncrypt -> concat([IV,JSON], [IV_SIZE,JSON_SIZE]) -> toString('base64')
Python加密示意:
AesEncrypt -> (bytes)IV+(bytes)Json -> base64.b64encode
附錄五:API模擬串接服務
提供模擬使用HTTP Protocol,透過POST方式傳遞資料到MYPAY, 並且得到回傳結果。