特約商店 MYPAY LINK PayPage交易
修改歷程
版本 | 異動日期 | 修訂內容 | 位置 |
---|---|---|---|
1.0 | 2021.01.01 | 新版文件 | |
1.0.1 | 2023.02.08 | 調整信用卡授權請款說明 | 20230208_01 |
1.0.2 | 2023.04.07 | 新增開發前請先閱讀 | 20230407_01 |
1.0.3 | 2023.05.31 | 新增定期扣款開發前請先閱讀 | 20230531_01 |
1.0.4 | 2023.07.26 | 新增特約商店可用支付工具 | 20230726_01 |
1.0.5 | 2023.10.11 | 金流交易回報,新增代收費模式回報 | 20231011_01 |
1.0.6 | 2023.11.22 | 金流交易回傳,狀態碼改變,超過5秒,http code =429 |
開發前請先閱讀
金鑰的注意事項
Q:金鑰的有效期限
A:MyPay的金鑰有效期限只有一年,如果要延長,請自行至金鑰管理系統延長
Q:金鑰可以在什麼時候延長
A:到期日7天前,都可以延長(不包含第7天),一但超過,系統自動產生新金鑰後,即不可再延長
Q:金鑰即將到期前7天時,系統新發的金鑰是否會於信件中夾帶
A:會
Q:金鑰在到期前何時會發信通知
A:定期發送金鑰到期通知的頻率為:60天/30天/20天,若 貴司皆無動作,則系統會於到期前七天產製新金鑰並發送email到 貴司技術窗口信箱,此時舊的金鑰,因還未過期還可以使用,一旦過期,貴司仍使用舊金鑰則會無法交易
交易結果的告知
Q:交易完成的導頁會有交易結果的參數嗎
A:不論是參數中的success_returl 或 failure_returl或是後臺設定的交易成功、失敗導頁網址,均不會給予交易結果參數
Q:何時會給予交易結果
A:MyPay將在接收上游結果後,會由附錄三這邊的設定,背景主動發動通知商家
Q:當我接到MyPay通知時該做什麼
A:請直接回應8888,如沒有回應,MyPay會通知5次,若5次內均得不到8888,系統會寄發信件給予商家的技術人員以及附錄三而外設定的人員
定期扣款常見問題
Q:電子錢包綁定後可否執行一次性交易+定期定額扣款
A:只能執行一次性交易,無法使用定期扣款
Q:.API發動定期定額扣款,扣款失敗的話,會重扣幾次,多久重扣一次
A:不論是API建檔,還是後臺建檔,預設都是3天後發動重扣,並且只會執行一次,如果要調整,需要至後台調整((參考下圖,如太小請按右鍵可另開分頁放大)),API並無支援調整
Q:.定期定額扣款到一半時,消費者若想換信用卡扣款可以嗎?可透過 API 實現?
A:可以更換卡號,請從後台更改(參考下圖,如太小請按右鍵可另開分頁放大),無法透過API修改,或著發動取消扣款API,從新建立新的
PayPage交易設計概要
安全性設計
所有的付費要求發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES加密,再透過HTTPS加密傳輸。交易資料不由消費者端送出,可確保資料不被消費者竄改。所有付費資訊經過MYPAY Link匝道進行轉送處理,特約商店不需要處理消費者的付費流程以及安全控管。
資料驗證
交易參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。 大部分交易模式,特約商店以導頁的方式處理付費流程,特約商店只需確保與MYPAY LINK連線正常,即可確保交易安全以及確保資料安全。
系統架構
所有請求與查詢只能透過伺服器發動,由特約商店的網頁伺服器利用伺服端服務程式發動交易,以避免交易資料被消費者竄改或攔截。為確保交易安全,MYPAY Link僅能接受 https connection,注意僅接受TLS1.2以上協定。
MYPAY LINK 目前提供之服務與格式
MYPAY LINK 提供多種交易與應用方式,應用情境如下:
(1)PayPage金流交易請求:透過MYPAY交易頁面作付款動作
(2)交易查詢:查詢金流交易資訊
(3)交易退款:金流交易退款,支援即時退款且未超過退款期限內之支付方式皆可使用此方式發動退款。
(4)信用卡取消授權:信用卡類交易,若使用自行請款模式,交易狀態為授權完成(245)時,則可以發動此信用卡取消授權,用以取消交易,並釋出消費者圈存金額。
(5)信用卡授權請款:信用卡類交易,若使用自行請款模式,交易狀態為授權完成(245)時,則可以發動此信用卡授權請款,用以完成交易。發動只限定一次。可全額或部分請款。
(6)取消定期定額式扣款:取消原有定期扣款訂單,亦可以從管理介面取消。
(7)MYPAY電子錢包綁定:以此API進行消費者信用卡綁定。
(8)取消退款:當該筆退款尚未送到金流服務商時,以取消之前發的退款訂單,若訂單已上傳到金流服務商則無效。
(9)電子發票開立:開立發票可在交易時一併發動請求 或交易成功後獨立發動開立發票。
(10)電子發票查詢:查詢電子發票開立狀態。
(11)特約商店可用支付工具查詢:可查詢特約商店可使用那些支付工具
我們提供介接方式是透過https連線,只接受POST方式傳送交易資料。
介接網址
特約商店模式
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/init |
正式區 | https://ka.mypay.tw/api/init |
Client模式(限定功能)
位置 | API介接網址 |
---|---|
測試區 | https://pay.usecase.cc/api/open |
正式區 | https://ka.mypay.tw/api/open |
資料加密方式
AES 256編碼 + base64編碼(附錄四資料加密方式)
加密金鑰
金鑰會透過mail發送,也可從管理後台取得
文字編碼
一律使用UTF-8相容編碼
PayPage金流交易
<?php
/**
* 特約商店串接-PayPage金流交易
*/
final class StoreOrder
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['items'] = [['id' => '1',
'name' => '商品名稱',
'cost' => '10',
'amount' => '1',
'total' => '10']];
$rawData['cost'] = 10;
$rawData['user_id'] = "phper";
$rawData['order_id'] = "1234567890";
$rawData['ip'] = "192.168.0.1"; // 此為消費者IP,會做為驗證用
$rawData['pfn'] = "0";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/orders'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreOrder = new StoreOrder();
$StoreOrder->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>
/// 特約商店串接-PayPage金流交易
/// </summary>
public class StoreOrder {
/// <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() {
StoreOrder simulator = new StoreOrder();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
ArrayList items = new ArrayList();
dynamic item = new ExpandoObject();
item.id = "1";
item.name = "商品名稱";
item.cost = "10";
item.amount = "1";
item.total = "10";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.items = items;
rawData.cost = "10";
rawData.user_id = "phper";
rawData.order_id = "1234567890";
rawData.ip = "127.0.0.1"; // 此為消費者IP,會做為驗證用
rawData.pfn = "0";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/orders";
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;
/**
* 特約商店串接-PayPage金流交易
* 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 StoreOrder {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreOrder simulator = new StoreOrder();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
ArrayList items = new ArrayList();
Map<Object, Object> item = new HashMap<Object, Object>();
item.put("id", "1");
item.put("name", "商品名稱");
item.put("cost", "10");
item.put("amount", "1");
item.put("total", "10");
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("items", items);
rawData.put("cost", "10");
rawData.put("user_id", "phper");
rawData.put("order_id", "1234567890");
rawData.put("ip", "127.0.0.1"); // 此為消費者IP,會做為驗證用
rawData.put("pfn", "0");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/orders");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-PayPage金流交易
*/
function StoreOrder() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreOrder.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: "0"
};
};
/**
* 取得服務位置
*/
StoreOrder.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/orders"
};
};
/**
* AES 256 加密
*/
StoreOrder.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 到主機
*/
StoreOrder.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();
});
};
/**
* 取得送出欄位資料
*/
StoreOrder.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreOrder.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreOrder = new StoreOrder();
StoreOrder.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-PayPage金流交易
"""
class StoreOrder:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'items': [{
'id': "1",
'name': "商品名稱",
'cost': "10",
'amount': "1",
'total': "10"
}],
'cost': "10",
'user_id': "phper",
'order_id': "1234567890",
'ip': "127.0.0.1",
'pfn': "0"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/orders'
}
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)
StoreOrder = StoreOrder()
StoreOrder.run()
回傳 JSON 結構如下:
{
"code": "200",
"msg": "資料正確",
"url": "http:\/\/pay.k20-mypay.tw\/payment\/81127.html",
"uid": 81127,
"key": "af4e678325dd49f8036949c38ab04105"
}
此交易請求參數內含交易訂單編號,與查詢用之金鑰,以及付費網址。貴司也可將此交易訂單編號綁定貴司的消費者訂單,我們會透過主動回報機制,以POST方式回報交易即時資訊,回報網址可以在管理系統中設定,啟動服務後,系統就會主動回傳訂單狀態。
特約商店『PayPage金流交易』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/orders"} JSON格式,AES256加密資料 |
encry_data | text | 『PayPage金流交易』欄位參考 JSON格式,AES256加密資料 |
『PayPage金流交易』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | 必填 |
user_id | string | 消費者帳號 請注意,記憶卡號跟記憶發票都是依此欄位為基準,如果不同的user來買,請記得切換,否則會出現同一筆資料 |
必填 |
user_name | string | 消費者姓名,電子錢包交易必要欄位 | |
user_real_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 | 消費者生日 | |
cost | string | 訂單總金額 = 物品之總價加總 + 折價 + 運費(如為定期定額交易,此為每一期的應扣金額) |
必填 |
currency | string | 預設交易幣別(預設為TWD新台幣) | 『幣別類型』值參考 |
enable_dcc | integer | 啟用dcc(自動換匯) | 『自動換匯』值參考 |
order_id | string | 訂單編號(訂單編號最長為50bytes) | 必填 |
ip | string | 消費者來源 IP(格式務必正確,部分金流服務商後續處理會驗證) | 必填 |
item | string | 訂單內物品數 | |
items | array | 訂單物品項目 | 必填 每筆『商品項目』欄位參考 |
regular | string | 定期定額付費,期數單位 | 『分期類型定義』值參考 |
regular_total | string | 總期數(如為 12 期即代入 12,如果不設定終止期,請代入 0) | |
regular_first_charge_date | string | 定期扣款起扣日 不得小於當日,若未指定日期,將判定為當日扣款 格式為 YYYYMMDD,如 20090916 |
|
group_id | string | 定期定額式扣款編號 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 | |
pfn | string | 預選付費方法 | 『付款方式』值參考 |
interface | string | 消費者操作介面類型 pc/app | |
discount | string | 折價金額 (預設0) | |
success_returl | string | 交易成功導頁網址 | |
failure_returl | string | 交易失敗導頁網址 | |
limit_pay_days | integer | 虛擬帳號與超商代碼使用之有效天數 | |
shipping_fee | string | 運費 | |
enable_quickpay | integer | 啟用快速結帳 | 『啟用快速結帳』值參考 |
enable_ewallet | integer | 啟用電子錢包 | 『啟用電子錢包』值參考 |
virtual_pan | string | 電子錢包虛擬卡號 | |
ewallet_type | integer | 電子錢包執行類型 | 『電子錢包執行類型』值參考 |
transaction_type | integer | 交易類型 | 『交易類型』值參考 |
creditcard_installment | string | 國內信用卡分期顯示限定 JSON 格式如下 { "3": ["013", "822", "808"], "6": ["822", "812"] } |
|
cardless_code | string | 無卡分期商品代碼 | |
cardless_installment | string | 無卡分期顯示消費者可選擇之期數,格式為陣列,例: [3, 6, 9, 12] |
|
each_code | string | eACH交易代碼(如果使用eACH必須指定交易代碼) | |
issue_invoice_state | integer | 開立發票 | 『電子發票是否開立狀態』值參考 |
invoice_ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_address | string | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_title_force | string | 若選擇實體發票時,發票抬頭無法異動。 | 『電子發票欄位異動』值參考 |
invoice_b2b_id_force | string | 若選擇實體發票時,統一編號無法異動。 | 『電子發票欄位異動』值參考 |
invoice_b2b_address_force | integer | 若選擇實體發票時,預設地址無法異動。 | 『電子發票欄位異動』值參考 |
user_data | object | 無卡分期消費者資訊 | 『無卡分期消費者資訊』欄位參考 |
files_path | string | 無卡分期上傳檔案路徑 (JSON多筆格式) | 『無卡分期檔案』值參考 |
price | string | 儲值交易之交易金額/點數 (儲值交易以此欄位辨識交易金額/點數) | |
recharge_code | string | 儲值交易時,使用當時儲值之儲值產品代碼 | |
recharge_items | array | 儲值交易用物品資訊 | 每筆『儲值交易項目』欄位參考 |
agent_sms_fee_type | integer | 經銷商代收費是否含簡訊費 | 『含不含簡訊費』值參考 |
agent_charge_fee_type | integer | 經銷商代收費是否含手續費 | 『含不含手續費類型』值參考 |
agent_charge_fee | integer | 經銷商代收費 | |
is_agent_charge | integer | 是否為經銷商代收費模式 若 is_agent_charge 有指定,以指定優先 若欄位agent_charge_fee有費用,或經銷商代收費是含簡訊費、或經銷商代收費含手續費,則預設為1 若欄位agent_charge_fee無費用,且經銷商代收費不含簡訊費、或經銷商代收費不含手續費或參欄位都未使用則預設為0 |
『是否為經銷商代收費模式』值參考 |
shipping_info | object | 送貨資訊 (後付款為必填) | 『後付款送貨資訊』欄位參考 |
creditcard_is_automatic_payment | string | 信用卡類交易時,是否使用授權請款模式(預設自動請款) | 『信用卡授權請款模式類型』值參考 |
『PayPage金流交易』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 交易回傳碼 | |
msg | string | 回傳訊息 | |
uid | string | 訂單之交易流水號(交易訂單/票券服務訂單/儲值訂單) | |
key | string | 交易驗証碼 | |
url | string | 交易網址 |
支援語系名稱 | 語系編碼 |
---|---|
繁體中文版 | zh-TW(預設) |
簡體中文版 | zh-CN |
英文版 | en |
印尼語 | id |
『交易完成回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
is_agent_charge | int | 是否為經銷商代收費模式 | 『是否為經銷商代收費模式』值參考 |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
installment | string | 信用卡分期資訊 JSON 格式 | 『分期資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 『超商條碼繳費』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『非即時交易回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
is_agent_charge | int | 是否為經銷商代收費模式 | 『是否為經銷商代收費模式』值參考 |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
installment | string | 信用卡分期資訊 JSON 格式 | 『分期資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
『訂單確認回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
card_type | string | 信用卡卡別 | 『信用卡別類型』值參考 |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
is_agent_charge | int | 是否為經銷商代收費模式 | 『是否為經銷商代收費模式』值參考 |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | integer | 交易類型 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
installment | string | 信用卡分期資訊 JSON 格式 | 『分期資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 銀行代碼 虛擬帳號資訊 |
|
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
交易查詢
<?php
/**
* 特約商店串接-交易查詢
*/
final class StoreQuery
{
/**
* 特約商店商務代號
* @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['uid'] = "34799";
$rawData['key'] = "c23dd140aa9536eb4c09ef053171110f";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/queryorder'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreQuery = new StoreQuery();
$StoreQuery->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 StoreQuery {
/// <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() {
StoreQuery simulator = new StoreQuery();
//僅限走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.uid = "34799";
rawData.key = "c23dd140aa9536eb4c09ef053171110f";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/queryorder";
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 StoreQuery {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreQuery simulator = new StoreQuery();
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("uid", "34799");
rawData.put("key", "c23dd140aa9536eb4c09ef053171110f");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/queryorder");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-交易查詢
*/
function StoreQuery() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreQuery.prototype.getRawData = function () {
return {
uid: "34799",
key: "c23dd140aa9536eb4c09ef053171110f"
};
};
/**
* 取得服務位置
*/
StoreQuery.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/queryorder"
};
};
/**
* AES 256 加密
*/
StoreQuery.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 到主機
*/
StoreQuery.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();
});
};
/**
* 取得送出欄位資料
*/
StoreQuery.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreQuery.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreQuery = new StoreQuery();
StoreQuery.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-交易查詢
"""
class StoreQuery:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'uid': "34799",
'key': "c23dd140aa9536eb4c09ef053171110f"
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/queryorder'
}
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)
StoreQuery = StoreQuery()
StoreQuery.run()
回傳 JSON 結構如下:
{
"key": "c011e403fa73b088ca3d50aacfa9c43a",
"prc": "250",
"cardno": "493817******0003",
"acode": "AA1234",
"issuing_bank": "合作金庫",
"issuing_bank_uid": "006",
"is_agent_charge": 0,
"transaction_mode": 1,
"supplier_name": "高鉅金融",
"supplier_code": "T0",
"order_id": "20200227165CFAB2C9",
"user_id": "userid",
"uid": 59665,
"cost": 10,
"currency": "TWD",
"actual_cost": 10,
"actual_currency": "TWD",
"love_cost": 0,
"retmsg": "付款完成",
"pay_mode_uid": 1,
"pfn": "CREDITCARD",
"trans_type": 1,
"redeem": "",
"installment": "",
"finishtime": "20200227165436",
"store_group_id": "",
"nois": "",
"payment_name": "",
"bank_id": "",
"result_type": 4,
"result_content_type": "CREDITCARD",
"result_content": "{}",
"expired_date": "",
"appropriation_date": "20200318",
"invoice_state": 2,
"invoice_date": "20200227165440",
"invoice_wordtrack": "YJ",
"invoice_number": "00100030",
"invoice_rand_code": "",
"invoice_seller_ban": "",
"invoice_buyer_ban": "",
"invoice_left_qrcode": "",
"invoice_middle_barcode": "",
"invoice_right_qrcode": "",
"invoice_title_type": 1,
"invoice_title": "",
"invoice_amount": "0",
"invoice_sales_amount": "0",
"invoice_tax_amount": "0",
"invoice_order_detail": "[]",
"invoice_ratetype": 1,
"invoice_input_type": 2,
"invoice_allowance": [],
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
發動交易後,如果遲遲未接收回報,可透過此方法查詢訂單交易。
特約商店『交易查詢』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/queryorder"} JSON格式,AES256加密資料 |
encry_data | text | 『交易查詢』欄位參考 JSON格式,AES256加密資料 |
『交易查詢』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | Payment Hub之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
finishtime | string | 交易完成時間(YYYYMMDDHHmmss) | |
cardno | string | 銀行端口回傳碼 | |
acode | string | 授權碼 | |
issuing_bank | string | 發卡行 | |
issuing_bank_uid | string | 發卡銀行代碼 | |
is_agent_charge | int | 是否為經銷商代收費模式 | 『是否為經銷商代收費模式』值參考 |
transaction_mode | integer | 交易服務類型 | 『交易服務類型』值參考 |
supplier_name | string | 交易之金融服務商 | |
supplier_code | string | 交易之金融服務商代碼 | 『金流供應商代碼』值參考 |
order_id | string | 貴特店系統的訂單編號 | |
user_id | string | 消費者帳號 | |
cost | string | 總交易金額 | |
currency | string | 原交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
price | string | 請求交易點數/金額 | |
actual_price | string | 實際交易點數/金額 | |
recharge_code | string | 交易產品代碼 | |
love_cost | string | 愛心捐款金額 | |
retmsg | string | 回傳訊息 | |
pfn | string | 付費方法 | |
trans_type | string | 付款種類 | 『交易類型定義』值參考 |
redeem | string | 紅利資訊 JSON 格式 | 『紅利資訊』值參考 |
installment | string | 信用卡分期資訊 JSON 格式 | 『分期資訊』值參考 |
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
bank_id | string | 虛擬帳號銀行代碼 | |
expired_date | string | 有效日期(YYYYMMDDHHmmss) 虛擬帳號、超商代碼、無卡分期資訊 |
|
appropriation_date | string | 預計撥款日期(YYYYMMDD) | |
result_type | integer | 虛擬帳號、超商代碼 資料格式類型 | 『閘道內容回傳格式類型』值參考 |
result_content_type | string | 資料內容所屬支付名稱 | 『資料內容所屬支付名稱』值參考 |
result_content | string | 虛擬帳號、超商代碼 資料內容 | 『虛擬帳號回傳欄位』值參考 『ibon』值參考 『FamiPort』值參考 『Life-ET』值參考 |
refund_order | array | 退款訂單資訊(多筆格式) | 每筆『交易查詢-退款資訊』欄位參考 |
cancel_order | array | 取消訂單資訊(多筆格式) | 每筆『交易查詢-取消資訊』欄位參考 |
invoice_state | integer | 發票開立狀態 | 『電子發票開立狀態類型』值參考 |
invoice_date | string | 發票開立日期(YYYYMMDD) | |
invoice_wordtrack | string | 發票字軌 | |
invoice_number | string | 發票號碼 | |
invoice_rand_code | string | 電子發票隨機碼 | |
invoice_seller_ban | string | 賣方統一編號 | |
invoice_buyer_ban | string | 買方統一編號 | |
invoice_left_qrcode | string | 電子發票左邊QrCode內容 | |
invoice_middle_barcode | string | 電子發票中間Barcode內容(格式Code-39) | |
invoice_right_qrcode | string | 電子發票右邊QrCode內容 | |
invoice_title_type | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_title | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
invoice_print_type | integer | 電子發票列印類型 | 『電子發票列印類型』值參考 |
invoice_print_device | integer | 電子發票列印設備 | 『電子發票列印設備』值參考 |
invoice_amount | string | 電子發票銷售總額 | |
invoice_sales_amount | string | 電子發票銷售額 | |
invoice_tax_amount | string | 電子發票稅額 | |
invoice_order_detail | string | 電子發票全部產品明細(JSON格式) | 『商品細項』值參考 |
invoice_ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_allowance | array | 電子發票折讓資訊 | 每筆『電子發票折讓資訊』欄位參考 |
items | array | 訂單商品項目 | 每筆『商品項目』欄位參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
發動交易後,如果遲遲未接收回報,可透過此方法查詢訂單交易。
交易退款
<?php
/**
* 特約商店串接-交易退款
*/
final class StoreRefund
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['uid'] = "88833";
$rawData['key'] = "1c943777706d68f757afdb9034213001";
$rawData['cost'] = 100;
$rawData['invoice_state'] = 6;
$rawData['items'][] = ['id' => '1',
'name' => '倚天劍模型',
'cost' => 100,
'amount' => 1,
'total' => 100];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/refund'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreRefund = new StoreRefund();
$StoreRefund->run();
?>
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Dynamic;
/// <summary>
/// 建議使用.net framework4.5以上版本
/// 1.請透過NuGet安裝Newtonsoft.Json,若.net framework小於4.5請安裝Newtonsoft.Json 7.0以下版本
/// 2.若貴司.net framework小於4 可能無法使用dynamic,若是自行組陣列
/// 3.若貴司.net framework小於3.5 可能無法使用AES類別,若是參閱微軟網站使用較舊方式進行加密
/// 4.若貴司.net framework小於3.5 可能沒有Linq可使用,故data只能組字串,Newtonsoft.Json也可能無法使用
/// </summary>
namespace MyPay {
/// <summary>
/// 特約商店串接-交易退款
/// </summary>
public class StoreRefund {
/// <summary>
/// 特約商店商務代號
/// </summary>
public string storeUid = "289151880002";
/// <summary>
/// 特約商店金鑰或認證碼
/// </summary>
public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/// <summary>
/// 串接交易位置
/// </summary>
public string url = "https://pay.usecase.cc/api/init";
/// <summary>
/// 執行
/// </summary>
static void Main() {
StoreRefund simulator = new StoreRefund();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
ArrayList items = new ArrayList();
dynamic item = new ExpandoObject();
item.id = "1";
item.name = "倚天劍模型";
item.cost = 100;
item.amount = 1;
item.total = 100;
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.uid = "88833";
rawData.key = "1c943777706d68f757afdb9034213001";
rawData.cost = 100;
rawData.invoice_state = 6;
rawData.items = items;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/refund";
return rawData;
}
/// <summary>
/// 取得送出欄位資料
/// </summary>
private NameValueCollection GetPostData() {
string data_json = JsonConvert.SerializeObject(GetRawData(), Formatting.None);
string svr_json = JsonConvert.SerializeObject(GetService(), Formatting.None);; //依API種類調整
//產生AES向量
var IV = GetBytesIV();
//進行加密
var data_encode = Encrypt(data_json, this.storeKey, IV);
var svr_encode = Encrypt(svr_json, this.storeKey, IV);
//請注意使用的 Http Post 套件是否會自動加上UrlEncode,本Post範例為原始方式,故須加上UrlEncode
//若自行使用的套件會自動補上UrlEncode,則請忽略下面的UrlEncode,避免做了兩次UrlEncode
string data_toUrlEncode = HttpUtility.UrlEncode(data_encode);
string svr_toUrlEncode = HttpUtility.UrlEncode(svr_encode);
NameValueCollection postData = new NameValueCollection();
postData["store_uid"] = this.storeUid;
postData["service"] = svr_toUrlEncode;
postData["encry_data"] = data_toUrlEncode;
return postData;
}
/// <summary>
/// AES 256 加密
/// </summary>
/// <param name="data"></param>
/// <param name="key"></param>
/// <param name="byteIV"></param>
/// <returns></returns>
private string Encrypt(string data, string key, byte[] byteIV) {
var byteKey = System.Text.Encoding.UTF8.GetBytes(key);
var enBytes = AES_Encrypt(data, byteKey, byteIV);
return Convert.ToBase64String(BytesAdd(byteIV, enBytes));
}
/// <summary>
/// AES 256 加密處理
/// </summary>
/// <param name="original"></param>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <returns></returns>
private byte[] AES_Encrypt(string original, byte[] key, byte[] iv) {
try {
var data = Encoding.UTF8.GetBytes(original);
var cipher = Aes.Create().CreateEncryptor(key, iv);
var de = cipher.TransformFinalBlock(data, 0, data.Length);
return de;
} catch {
return null;
}
}
/// <summary>
/// 轉換Bytes
/// </summary>
/// <param name="a"></param>
/// <param name="arryB"></param>
/// <returns></returns>
private byte[] BytesAdd(byte[] a, params byte[][] arryB) {
List < byte > c = new List < byte > ();
c.AddRange(a);
arryB.ToList().ForEach(b => {
c.AddRange(b);
});
return c.ToArray();
}
/// <summary>
/// 產生AES的IV
/// </summary>
/// <returns></returns>
private static byte[] GetBytesIV() {
var aes = System.Security.Cryptography.AesCryptoServiceProvider.Create();
aes.KeySize = 256;
aes.GenerateIV();
return aes.IV;
}
/// <summary>
/// 資料 POST 到主機
/// </summary>
/// <param name="pars"></param>
/// <returns></returns>
private string Post(NameValueCollection pars) {
string result = string.Empty;
string param = string.Empty;
if (pars.Count > 0) {
pars.AllKeys.ToList().ForEach(key => {
param += key + "=" + pars[key] + "&";
});
if (param[param.Length - 1] == '&') {
param = param.Remove(param.Length - 1);
}
}
byte[] bs = Encoding.UTF8.GetBytes(param);
try {
HttpWebRequest req = (HttpWebRequest) HttpWebRequest.Create(this.url);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
req.ContentLength = bs.Length;
using(Stream reqStream = req.GetRequestStream()) {
reqStream.Write(bs, 0, bs.Length);
}
using(WebResponse wr = req.GetResponse()) {
Encoding myEncoding = Encoding.GetEncoding("UTF-8");
using(StreamReader myStreamReader = new StreamReader(wr.GetResponseStream(), myEncoding)) {
result = myStreamReader.ReadToEnd();
}
}
req = null;
} catch (WebException ex) {
throw new WebException(ex.Message + "params : " + param, ex, ex.Status, ex.Response);
}
return result;
}
}
/// <summary>
/// 串接服務請求欄位
/// </summary>
public class ServiceRequest {
public string service_name { get; set; }
public string cmd { get; set; }
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.URLEncoder;
import java.util.*;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static java.nio.charset.StandardCharsets.UTF_8;
import org.spongycastle.crypto.engines.AESEngine;
import org.spongycastle.crypto.modes.CBCBlockCipher;
import org.spongycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.spongycastle.crypto.params.KeyParameter;
import org.spongycastle.crypto.params.ParametersWithIV;
import java.security.SecureRandom;
/**
* 特約商店串接-交易退款
* 1. jackson-core 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
* 2. jackson-databind 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
* 3. jackson-annotations 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
* 4. Spongy Castle 下載 https://mvnrepository.com/artifact/com.madgag.spongycastle/core
*/
public class StoreRefund {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreRefund simulator = new StoreRefund();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
ArrayList items = new ArrayList();
Map<Object, Object> item = new HashMap<Object, Object>();
item.put("id", "1");
item.put("name", "倚天劍模型");
item.put("cost", 100);
item.put("amount", 1);
item.put("total", 100);
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("uid", "88833");
rawData.put("key", "1c943777706d68f757afdb9034213001");
rawData.put("cost", 100);
rawData.put("invoice_state", 6);
rawData.put("items", items);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/refund");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-交易退款
*/
function StoreRefund() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreRefund.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "88833",
key: "1c943777706d68f757afdb9034213001",
cost: 100,
invoice_state: 6,
items: [
{
'id': '1',
'name': '倚天劍模型',
'cost': 100,
'amount': 1,
'total': 100
}
],
};
};
/**
* 取得服務位置
*/
StoreRefund.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/refund"
};
};
/**
* AES 256 加密
*/
StoreRefund.prototype.encrypt = function (fields, key) {
let eData = JSON.stringify(fields);
const blockSize = 16;
const iv = crypto.randomBytes(blockSize);
const encryptor = crypto.createCipheriv('aes-256-cbc', key, iv);
let tmpCipher = encryptor.update(Buffer.from(eData));
let finalCipher = encryptor.final();
const tempData = Buffer.concat([tmpCipher, finalCipher], tmpCipher.length + finalCipher.length);
let data = Buffer.concat([iv, tempData], iv.length + tempData.length).toString('base64');
return data;
};
/**
* 資料 POST 到主機
*/
StoreRefund.prototype.post = function (postData) {
return new Promise((res, rej) => {
let options = {
method: "POST",
headers: {
"Content-Type": "application/json"
},
rejectUnauthorized: false
};
let send_process = httpRequest.request(this.url, options, (api_res) => {
let res_data = "";
api_res.on('data', (tmp_data) => {
res_data += tmp_data;
});
api_res.on('end', () => {
res(res_data);
});
});
send_process.write(JSON.stringify(postData));
send_process.end();
});
};
/**
* 取得送出欄位資料
*/
StoreRefund.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreRefund.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreRefund = new StoreRefund();
StoreRefund.run();
import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes
"""特約商店串接-交易退款
"""
class StoreRefund:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "88833",
'key': "1c943777706d68f757afdb9034213001",
'cost': 100,
'invoice_state': 6,
'items': [
{
'id': '1',
'name': '倚天劍模型',
'cost': 100,
'amount': 1,
'total': 100
}
],
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/refund'
}
def encrypt(self, fields, key):
"""AES 256 加密
Args:
fields {dict}: 欄位資料
key {bytes}: AES金鑰
Returns:
{string}: 加密資料
"""
data = json.dumps(fields, separators=(',', ':'))
data = Padding.pad(data.encode('utf-8'), AES.block_size)
iv = get_random_bytes(AES.block_size)
cipher = AES.new(key, AES.MODE_CBC, iv)
data = cipher.encrypt(data)
data = base64.b64encode(iv + data)
return data
def post(self, postData):
"""資料 POST 到主機
Args:
postData {dict}: 欄位資料
Returns:
{string}: JSON資料
"""
result = requests.post(self.url, postData)
return result.text
def getPostData(self):
"""取得送出欄位資料
Returns:
{dict}: 欄位資料
"""
postData = {
'store_uid': self.storeUid,
'service': self.encrypt(self.getService(), self.storeKey),
'encry_data': self.encrypt(self.getRawData(), self.storeKey)
}
return postData
def run(self):
"""執行
"""
json = self.post(self.getPostData())
print(json)
StoreRefund = StoreRefund()
StoreRefund.run()
回傳 JSON 結構如下:
{
"row_data": {
"uid": 88833,
"refund_uid": 88834,
"key": "1c943777706d68f757afdb9034213001",
"prc": "230",
"finishtime": "20210701104600",
"order_id": "20210701107C23AA66",
"user_id": "userid",
"cost": 100,
"currency": "TWD",
"actual_cost": 100,
"actual_currency": "TWD",
"retmsg": "退款完成",
"pfn": "CREDITCARD",
"payment_name": "",
"nois": "",
"group_id": "",
"refund_type": 1,
"expected_refund_date": "",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
},
"key": "1c943777706d68f757afdb9034213001",
"uid": "88833",
"code": "B200",
"msg": "執行成功"
}
若需要退款時,發動此API提出退款請求。支援即時退款且未超過退款期限內之支付方式皆可使用此方式發動退款。 支援即時退款的支付方式有信用卡、美國運通、LINEPay、Pi錢包、街口支付、微信支付、支付寶、悠遊付、現金、GooglePay、ApplePay。
特約商店『交易退款』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/refund"} JSON格式,AES256加密資料 |
encry_data | text | 『交易退款』欄位參考 JSON格式,AES256加密資料 |
『交易退款』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店代碼 | |
key | string | 交易驗証碼 | |
uid | string | 訂單編號(UID) | |
cost | string | 退款金額(可部分退款) | |
invoice_state | integer | 若有開立電子發票,指定電子發票使用作廢或折讓 注意:跨發票月份無法作廢 |
『電子發票退款時使用作廢或折讓』值參考 |
items | array | 退款項目 若使用電子發票,此欄位必填 退款項目之項目名稱必須和交易時之產品項目名稱相同 退款項目之總金額必須與退款金額一致 |
每筆『商品項目』欄位參考 |
『交易退款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
key | string | 特約商店驗證碼 (store_token) | |
uid | string | 訂單編號(UID) | |
code | string | 處理狀態 B200 或 B500 | |
msg | string | 回傳訊息 | |
row_data | object | 退款資訊(即時退款才有此資訊) | 『退款完成回傳資訊』欄位參考 |
信用卡取消授權
<?php
/**
* 特約商店串接-信用卡取消授權
*/
final class StoreCreditcardCancelAuthorized
{
/**
* 特約商店商務代號
* @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['uid'] = "103274";
$rawData['key'] = "c1482acd0f7b459ee17b12c29660d582";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/cancelauthorization'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreCreditcardCancelAuthorized = new StoreCreditcardCancelAuthorized();
$StoreCreditcardCancelAuthorized->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 StoreCreditcardCancelAuthorized {
/// <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() {
StoreCreditcardCancelAuthorized simulator = new StoreCreditcardCancelAuthorized();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.uid = "103274";
rawData.key = "c1482acd0f7b459ee17b12c29660d582";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/cancelauthorization";
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 StoreCreditcardCancelAuthorized {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCreditcardCancelAuthorized simulator = new StoreCreditcardCancelAuthorized();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("uid", "103274");
rawData.put("key", "c1482acd0f7b459ee17b12c29660d582");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/cancelauthorization");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-信用卡取消授權
*/
function StoreCreditcardCancelAuthorized() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCreditcardCancelAuthorized.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "103274",
key: "c1482acd0f7b459ee17b12c29660d582",
};
};
/**
* 取得服務位置
*/
StoreCreditcardCancelAuthorized.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/cancelauthorization"
};
};
/**
* AES 256 加密
*/
StoreCreditcardCancelAuthorized.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 到主機
*/
StoreCreditcardCancelAuthorized.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCreditcardCancelAuthorized.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCreditcardCancelAuthorized.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCreditcardCancelAuthorized = new StoreCreditcardCancelAuthorized();
StoreCreditcardCancelAuthorized.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 StoreCreditcardCancelAuthorized:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "103274",
'key': "c1482acd0f7b459ee17b12c29660d582",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/cancelauthorization'
}
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)
StoreCreditcardCancelAuthorized = StoreCreditcardCancelAuthorized()
StoreCreditcardCancelAuthorized.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功,取消授權完成",
"content": {
"key": "c1482acd0f7b459ee17b12c29660d582",
"prc": "300",
"cardno": "493817******0003",
"acode": "185804",
"card_type": "1",
"issuing_bank": "合作金庫",
"issuing_bank_uid": "006",
"transaction_mode": 1,
"supplier_name": "凱基銀行",
"supplier_code": "BC",
"order_id": "1234567890",
"user_id": "phper",
"uid": 103274,
"cost": 10,
"currency": "TWD",
"actual_cost": 10,
"actual_currency": "TWD",
"voucher_paid": [],
"voucher_free": [],
"price": 0,
"actual_price": 0,
"recharge_code": "",
"love_cost": 0,
"retmsg": "交易失敗",
"pay_mode_uid": 1,
"pfn": "CREDITCARD",
"trans_type": 1,
"redeem": "",
"installment": "",
"finishtime": "",
"store_group_id": "",
"group_id": "",
"nois": "",
"payment_name": "",
"bank_id": "",
"result_type": 4,
"result_content_type": "CREDITCARD",
"result_content": "{}",
"expired_date": "",
"appropriation_date": "",
"invoice_state": 0,
"invoice_date": "20220301151856",
"invoice_wordtrack": "JH",
"invoice_number": "00000011",
"invoice_rand_code": "9899",
"invoice_seller_ban": "28915188",
"invoice_buyer_ban": "",
"invoice_left_qrcode": "JH00000011111030198990000000a0000000a0000000028915188qKBLmT8adunB+e9m7isLNQ==:**********:1:1:1:倚天劍模型:1:10:",
"invoice_middle_barcode": "11104JH000000119899",
"invoice_right_qrcode": "**",
"invoice_title_type": 1,
"invoice_title": "123",
"invoice_print_type": 0,
"invoice_print_device": 0,
"invoice_amount": "10",
"invoice_sales_amount": "10",
"invoice_tax_amount": "0",
"invoice_order_detail": "[{\"Description\":\"倚天劍模型\",\"Quantity\":\"1\",\"UnitPrice\":\"10\",\"Amount\":\"10\"}]",
"invoice_ratetype": 1,
"invoice_input_type": 1,
"invoice_cloud_type": 2,
"invoice_mobile_code": "\/NFVIAZP",
"invoice_tax_id": "",
"invoice_natural_person": "",
"invoice_love_code": "",
"invoice_b2b_title": "",
"invoice_b2b_id": "",
"invoice_b2b_post_zone": "",
"invoice_b2b_address": "",
"invoice_allowance": [
{
"uid": 103274,
"amount": "10",
"order_detail": "[{\"SeqNo\":\"0\",\"ItemID\":\"103274\",\"ItemName\":\"倚天劍模型\",\"Qty\":\"1\",\"TotalAmount\":\"10\",\"RateType\":\"1\"}]"
}
],
"refund_order": [],
"cancel_order": [],
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
}
信用卡類交易,若使用自行請款模式,當交易狀態為授權完成(245)時,則可以發動此信用卡來進行取消授權,用以取消交易,並釋出消費者圈存金額。
特約商店『信用卡取消授權』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/cancelauthorization"} JSON格式,AES256加密資料 |
encry_data | text | 『信用卡取消授權』欄位參考 JSON格式,AES256加密資料 |
『信用卡取消授權』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店編號 | 必填 |
uid | string | 交易流水號 | |
key | string | 交易驗証碼 |
『信用卡取消授權』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 交易回傳碼 | |
msg | string | 回傳訊息 | |
content | object | 查詢內容 | 『交易查詢』欄位參考 |
信用卡授權請款
<?php
/**
* 特約商店串接-信用卡授權請款
*/
final class StoreCreditcardRequestPayment
{
/**
* 特約商店商務代號
* @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['uid'] = "103283";
$rawData['key'] = "54886f30612771f8f10d94499a41a347";
$rawData['cost'] = 9;
$rawData['items'] = [
[
'id' => '1',
'name' => '商品名稱',
'cost' => '9',
'amount' => '1',
'total' => '9'
]
];
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/authorizedrequestpayment'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreCreditcardRequestPayment = new StoreCreditcardRequestPayment();
$StoreCreditcardRequestPayment->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 StoreCreditcardRequestPayment {
/// <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() {
StoreCreditcardRequestPayment simulator = new StoreCreditcardRequestPayment();
//僅限走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 = "1";
item.amount = "1";
item.total = "1";
items.Add(item);
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.uid = "103283";
rawData.key = "54886f30612771f8f10d94499a41a347";
rawData.cost = 9;
rawData.items = items;
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/authorizedrequestpayment";
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 StoreCreditcardRequestPayment {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreCreditcardRequestPayment simulator = new StoreCreditcardRequestPayment();
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", "9");
item.put("amount", "1");
item.put("total", "9");
items.add(item);
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("uid", "103283");
rawData.put("key", "54886f30612771f8f10d94499a41a347");
rawData.put("cost", 9);
rawData.put("items", items);
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/authorizedrequestpayment");
return rawData;
}
/**
* AES 256 加密
* @param rawData 原始資料
* @param AesKey AES256金鑰字串
* @return 轉換成Base64資料
*/
public String encrypt(Map rawData, String AesKey) {
try {
ObjectMapper objMapper = new ObjectMapper();
byte[] data = objMapper.writeValueAsString(rawData).getBytes(UTF_8);
byte[] key = AesKey.getBytes(UTF_8);
// 16 bytes is the IV size for AES256
PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(
new CBCBlockCipher(new AESEngine()));
// Random iv
SecureRandom rng = new SecureRandom();
byte[] ivBytes = new byte[16];
rng.nextBytes(ivBytes);
cipher.init(true, new ParametersWithIV(new KeyParameter(key),
ivBytes));
byte[] outBuf = new byte[cipher.getOutputSize(data.length)];
int processed = cipher
.processBytes(data, 0, data.length, outBuf, 0);
processed += cipher.doFinal(outBuf, processed);
byte[] outBuf2 = new byte[processed + 16]; // Make room for iv
System.arraycopy(ivBytes, 0, outBuf2, 0, 16); // Add iv
System.arraycopy(outBuf, 0, outBuf2, 16, processed);
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(outBuf2);
return base64;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 資料 POST 到主機
* @param qstr 串接資料
* @return 服務回傳JSON資訊
*/
public String post(String qstr) {
String result = "";
try {
// 資料
byte[] qstr_bytes = qstr.getBytes(StandardCharsets.UTF_8);
URL iurl = new URL(this.url);
SSLContext sc = SSLContext.getInstance("TLSv1.2"); // $NON-NLS-1$
sc.init(null, null, new java.security.SecureRandom());
HttpsURLConnection con = (HttpsURLConnection) iurl.openConnection();
con.setSSLSocketFactory(sc.getSocketFactory());
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
con.setRequestProperty("Content-Length",
String.valueOf(qstr_bytes.length));
con.setRequestProperty("Accept-Charset", "UTF-8");
con.setDoOutput(true);
con.setDoInput(true);
con.getOutputStream()
.write(qstr.getBytes(Charset.forName("UTF-8")));
con.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(
con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
response.append(inputLine + "\r\n");
}
try {
result = response.toString();
} finally {
in.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return result;
}
/**
* 取得送出欄位資料
* @return POST完整資料
*/
public String getPostData() {
String postData = "";
try {
// Base64需要使用UrlEncode做傳輸
String data_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getRawData(), this.storeKey), "UTF-8");
String svr_toUrlEncode = URLEncoder.encode(
this.encrypt(this.getService(), this.storeKey), "UTF-8");
postData = "store_uid=" + this.storeUid + "&service="
+ svr_toUrlEncode + "&encry_data=" + data_toUrlEncode;
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return postData;
}
}
const crypto = require('crypto');
const httpRequest = require('https');
/**
* 特約商店串接-信用卡授權請款
*/
function StoreCreditcardRequestPayment() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreCreditcardRequestPayment.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "103283",
key: "54886f30612771f8f10d94499a41a347",
cost: 9,
items: [
{
'id': "1",
'name': "商品名稱",
'cost': "9",
'amount': "1",
'total': "9"
}
],
};
};
/**
* 取得服務位置
*/
StoreCreditcardRequestPayment.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/authorizedrequestpayment"
};
};
/**
* AES 256 加密
*/
StoreCreditcardRequestPayment.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 到主機
*/
StoreCreditcardRequestPayment.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();
});
};
/**
* 取得送出欄位資料
*/
StoreCreditcardRequestPayment.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreCreditcardRequestPayment.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreCreditcardRequestPayment = new StoreCreditcardRequestPayment();
StoreCreditcardRequestPayment.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 StoreCreditcardRequestPayment:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "103283",
'key': "54886f30612771f8f10d94499a41a347",
'cost': 9,
'items': [
{
'id': "1",
'name': "商品名稱",
'cost': "9",
'amount': "1",
'total': "9"
}
],
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/authorizedrequestpayment'
}
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)
StoreCreditcardRequestPayment = StoreCreditcardRequestPayment()
StoreCreditcardRequestPayment.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功,請款處理進行中。",
"content": {
"key": "54886f30612771f8f10d94499a41a347",
"prc": "247",
"cardno": "493817******0003",
"acode": "063218",
"card_type": "1",
"issuing_bank": "合作金庫",
"issuing_bank_uid": "006",
"transaction_mode": 1,
"supplier_name": "凱基銀行",
"supplier_code": "BC",
"order_id": "1234567890",
"user_id": "phper",
"uid": 103283,
"cost": 10,
"currency": "TWD",
"actual_cost": 0,
"actual_currency": "TWD",
"voucher_paid": [],
"voucher_free": [],
"price": 0,
"actual_price": 0,
"recharge_code": "",
"love_cost": 0,
"retmsg": "請款進行中",
"pay_mode_uid": 1,
"pfn": "CREDITCARD",
"trans_type": 1,
"redeem": "",
"installment": "",
"finishtime": "",
"store_group_id": "",
"group_id": "",
"nois": "",
"payment_name": "",
"bank_id": "",
"result_type": 4,
"result_content_type": "CREDITCARD",
"result_content": "{}",
"expired_date": "",
"appropriation_date": "",
"invoice_state": 0,
"invoice_date": "20220301160234",
"invoice_wordtrack": "JH",
"invoice_number": "00000014",
"invoice_rand_code": "4224",
"invoice_seller_ban": "28915188",
"invoice_buyer_ban": "",
"invoice_left_qrcode": "JH00000014111030142240000000a0000000a0000000028915188DdkbPtMzNyH4rp0qk7dRig==:**********:1:1:1:商品名稱:1:10:",
"invoice_middle_barcode": "11104JH000000144224",
"invoice_right_qrcode": "**",
"invoice_title_type": 1,
"invoice_title": "123",
"invoice_print_type": 0,
"invoice_print_device": 0,
"invoice_amount": "10",
"invoice_sales_amount": "10",
"invoice_tax_amount": "0",
"invoice_order_detail": "[{\"Description\":\"商品名稱\",\"Quantity\":\"1\",\"UnitPrice\":\"10\",\"Amount\":\"10\"}]",
"invoice_ratetype": 1,
"invoice_input_type": 1,
"invoice_cloud_type": 2,
"invoice_mobile_code": "\/NFVIAZP",
"invoice_tax_id": "",
"invoice_natural_person": "",
"invoice_love_code": "",
"invoice_b2b_title": "",
"invoice_b2b_id": "",
"invoice_b2b_post_zone": "",
"invoice_b2b_address": "",
"invoice_allowance": [],
"refund_order": [],
"cancel_order": [],
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
}
授權請款天數限制
1.請款有天數限制,依照銀行跟代收及非代收的情況,而有所不同,一旦超過請款天數限制,則無法請款,只能重新交易
2.代收付特約商店:一律5天內須要發動請款。
3.非代收付特約商店:依據不同銀行,天數不同,請參考下表,如不再此表內的銀行,代表未支援
銀行名稱 | 天數 |
---|---|
凱基銀行 | 5天 |
台新銀行 | 5天 |
國泰銀行 | 5天 |
特約商店『信用卡授權請款』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/authorizedrequestpayment"} JSON格式,AES256加密資料 |
encry_data | text | 『信用卡授權請款』欄位參考 JSON格式,AES256加密資料 |
『信用卡授權請款』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店編號 | 必填 |
uid | string | 交易流水號 | |
key | string | 交易驗証碼 | |
cost | integer | 總請款金額 | |
items | array | 請款的產品項目(商品名稱必須與原交易時商品名稱相同) 需帶入完整的實際請款產品項目 |
每筆『商品項目』欄位參考 |
『信用卡授權請款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 交易回傳碼 | |
msg | string | 回傳訊息 | |
content | object | 查詢內容 | 『交易查詢』欄位參考 |
定期定額式扣款取消
當消費者希望取消定期扣款,用此方法發動取消繼續扣款。
<?php
/**
* 特約商店串接-取消定期分期
*/
final class StoreDirectDebitDisabled
{
/**
* 特約商店商務代號
* @var string
*/
public $storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
* @var string
*/
public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
* @var string
*/
public $url = "https://pay.usecase.cc/api/init";
/**
* 取得串接欄位資料
* @return array
*/
public function getRawData()
{
$rawData = array();
$rawData['store_uid'] = $this->storeUid;
$rawData['order_id'] = "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F";
$rawData['group_id'] = "D0000000520";
$rawData['stop_time'] = "20180920";
$rawData['stop_reason'] = "消費者不喜翻~";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/disabledirectdebit'
);
}
/**
* 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;
}
}
$StoreDirectDebitDisabled = new StoreDirectDebitDisabled();
$StoreDirectDebitDisabled->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 StoreDirectDebitDisabled {
/// <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() {
StoreDirectDebitDisabled simulator = new StoreDirectDebitDisabled();
//僅限走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.order_id = "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F";
rawData.group_id = "D0000000520";
rawData.stop_time = "20180920";
rawData.stop_reason = "消費者不喜翻~";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/disabledirectdebit";
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 StoreDirectDebitDisabled {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreDirectDebitDisabled simulator = new StoreDirectDebitDisabled();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("order_id", "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F");
rawData.put("group_id", "D0000000520");
rawData.put("stop_time", "20180920");
rawData.put("stop_reason", "消費者不喜翻~");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/disabledirectdebit");
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 StoreDirectDebitDisabled() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreDirectDebitDisabled.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
order_id: "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F",
group_id: "D0000000520",
stop_time: "20180920",
stop_reason: "消費者不喜翻~",
};
};
/**
* 取得服務位置
*/
StoreDirectDebitDisabled.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/disabledirectdebit"
};
};
/**
* AES 256 加密
*/
StoreDirectDebitDisabled.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 到主機
*/
StoreDirectDebitDisabled.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();
});
};
/**
* 取得送出欄位資料
*/
StoreDirectDebitDisabled.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreDirectDebitDisabled.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreDirectDebitDisabled = new StoreDirectDebitDisabled();
StoreDirectDebitDisabled.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 StoreDirectDebitDisabled:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'order_id': "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F",
'group_id': "D0000000520",
'stop_time': "20180920",
'stop_reason': "消費者不喜翻~",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/disabledirectdebit'
}
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)
StoreDirectDebitDisabled = StoreDirectDebitDisabled()
StoreDirectDebitDisabled.run()
回傳 JSON 結構如下:
{
"code": "B500",
"msg": "停扣日必須大於今日",
"order_id": "F3DCB0AD-23F3-4721-BBBA-FAC8CDDB9E5F",
"group_id": "D0000000520"
}
特約商店『定期定額式扣款取消』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/disabledirectdebit"} JSON格式,AES256加密資料 |
encry_data | text | 『定期定額式扣款取消』欄位參考 JSON格式,AES256加密資料 |
『定期定額式扣款取消』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店商務代號 | |
order_id | string | 定期扣款之訂單編號 | |
group_id | string | 定期扣款群組編號 | |
stop_time | string | 取消日期 (時間格是為YYYYMMDD) | |
stop_reason | string | 取消原因 |
『定期定額式扣款取消』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 回傳碼 | |
msg | string | 訊息說明 | |
order_id | string | 定期扣款之訂單編號 | |
group_id | string | 定期扣款群組編號 |
MYPAY電子錢包綁定
特店產生一組token專屬會員,以後會員後續交易時,不需要再輸入卡號,加速交易流程與資訊安全。 發動API呼叫信用卡綁定頁面,消費者將信用卡號輸入,系統會綁定貴司電子錢包時,當完成時將畫面導回貴司網站,並會背景通報結果。
<?php
/**
* 特約商店串接-MYPAY電子錢包綁定
*/
final class StoreEWalletBind
{
/**
* 特約商店商務代號
* @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['user_id'] = "phper";
$rawData['user_sn'] = "A123456789";
$rawData['user_cellphone_code'] = "886";
$rawData['user_cellphone'] = "918123123";
$rawData['user_real_name'] = "金城武";
$rawData['user_email'] = "[email protected]";
$rawData['user_english_name'] = "Takeshi Kaneshiro";
$rawData['user_address'] = "台中市西區台灣大道二段573號6樓E室";
$rawData['pfn'] = "CREDITCARD";
$rawData['ip'] = "127.0.0.1";
$rawData['success_returl'] = "http://www.mypay.com.tw/";
$rawData['failure_returl'] = "http://www.google.com.tw/";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/ewalletbind'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreEWalletBind = new StoreEWalletBind();
$StoreEWalletBind->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>
/// 特約商店串接-MYPAY電子錢包綁定
/// </summary>
public class StoreEWalletBind {
/// <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() {
StoreEWalletBind simulator = new StoreEWalletBind();
//僅限走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.user_id = "phper";
rawData.user_sn = "A123456789";
rawData.user_cellphone_code = "886";
rawData.user_cellphone = "918123123";
rawData.user_real_name = "金城武";
rawData.user_email = "[email protected]";
rawData.user_english_name = "Takeshi Kaneshiro";
rawData.user_address = "台中市西區台灣大道二段573號6樓E室";
rawData.pfn = "CREDITCARD";
rawData.ip = "127.0.0.1";
rawData.success_returl = "http://www.mypay.com.tw/";
rawData.failure_returl = "http://www.google.com.tw/";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/ewalletbind";
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');
/**
* 經銷商串接-MYPAY電子錢包綁定
*/
function AgentEWalletBind() {
// 經銷商商務代號
this.agentUid = "518169081001";
// 經銷商金鑰或認證碼
this.agentKey = "2MrhtRYeF9wKZ4bKwzZoL8aZf26pWzIG";
// 特約商店商務代號
this.storeUid = "289151880002";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/agent";
};
/**
* 取得串接欄位資料
*/
AgentEWalletBind.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
user_id: "phper",
user_sn: "A123456789",
user_cellphone_code: "886",
user_cellphone: "918123123",
user_real_name: "金城武",
user_email: "[email protected]",
user_english_name: "Takeshi Kaneshiro",
user_address: "台中市西區台灣大道二段573號6樓E室",
pfn: "CREDITCARD",
ip: "127.0.0.1",
success_returl: "http://www.mypay.com.tw/",
failure_returl: "http://www.google.com.tw/",
};
};
/**
* 取得服務位置
*/
AgentEWalletBind.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/ewalletbind"
};
};
/**
* AES 256 加密
*/
AgentEWalletBind.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 到主機
*/
AgentEWalletBind.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();
});
};
/**
* 取得送出欄位資料
*/
AgentEWalletBind.prototype.getPostData = function () {
return {
"agent_uid": this.agentUid,
"service": this.encrypt(this.getService(), this.agentKey),
"encry_data": this.encrypt(this.getRawData(), this.agentKey)
};
};
/**
* 執行
*/
AgentEWalletBind.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
AgentEWalletBind = new AgentEWalletBind();
AgentEWalletBind.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
"""特約商店串接-MYPAY電子錢包綁定
"""
class StoreEWalletBind:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'user_id': "phper",
'user_sn': "A123456789",
'user_cellphone_code': "886",
'user_cellphone': "918123123",
'user_real_name': "金城武",
'user_email': "[email protected]",
'user_english_name': "Takeshi Kaneshiro",
'user_address': "台中市西區台灣大道二段573號6樓E室",
'pfn': "CREDITCARD",
'ip': "127.0.0.1",
'success_returl': "http://www.mypay.com.tw/",
'failure_returl': "http://www.google.com.tw/",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/ewalletbind'
}
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)
StoreEWalletBind = StoreEWalletBind()
StoreEWalletBind.run()
回傳 JSON 結構如下:
{
"code": "B200",
"msg": "執行成功",
"url": "https:\/\/pay.usecase.cc\/ewallet\/107176.html"
}
特約商店『MYPAY電子錢包綁定』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/ewalletbind"} JSON格式,AES256加密資料 |
encry_data | text | 『MYPAY電子錢包綁定』欄位參考 JSON格式,AES256加密資料 |
『MYPAY電子錢包綁定』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | 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 | 必填 |
pfn | string | 綁定支付方式 | 『付款方式』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 | |
success_returl | string | 綁定成功導頁網址 | |
failure_returl | string | 綁定失敗導頁網址 |
『MYPAY電子錢包綁定』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
code | string | 交易回傳碼 | 『電子錢包執行狀態碼』值參考 |
msg | string | 回傳訊息 | |
url | string | 交易網址 |
『電子錢包虛擬卡號回傳資訊』回報欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
pfn | string | 支付類型 | |
cardno | string | 綁定卡號 (僅顯示前六後四) | |
user_id | string | 消費者帳號 | |
cellphone_code | string | 用以取消綁定之手機國碼 | |
cellphone | string | 用以取消綁定之手機號碼 | |
virtual_pan | string | 虛擬卡號 | |
enable | string | 是否啟用 0.關閉 1.啟用 |
取消退款
當天發動退款請求時,會在系統退款柱列等待退款。系統退款時間為隔天凌晨零時才發動退款,故在尚未做退款作業前,可發動此API請求,來取消(5)退款請求(也可透過管理介面來取消)。
<?php
/**
* 特約商店串接-交易退款取消
*/
final class StoreRefundCancel
{
/**
* 特約商店商務代號
* @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['uid'] = "30587";
$rawData['key'] = "2b848830072c0db6273d5637ad4b8337";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/refundcancel'
);
}
/**
* 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;
}
}
$StoreRefundCancel = new StoreRefundCancel();
$StoreRefundCancel->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 StoreRefundCancel {
/// <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() {
StoreRefundCancel simulator = new StoreRefundCancel();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.uid = "30587";
rawData.key = "2b848830072c0db6273d5637ad4b8337";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/refundcancel";
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 StoreRefundCancel {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreRefundCancel simulator = new StoreRefundCancel();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("uid", "30587");
rawData.put("key", "2b848830072c0db6273d5637ad4b8337");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/refundcancel");
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 StoreRefundCancel() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreRefundCancel.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "30587",
key: "2b848830072c0db6273d5637ad4b8337",
};
};
/**
* 取得服務位置
*/
StoreRefundCancel.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/refundcancel"
};
};
/**
* AES 256 加密
*/
StoreRefundCancel.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 到主機
*/
StoreRefundCancel.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();
});
};
/**
* 取得送出欄位資料
*/
StoreRefundCancel.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreRefundCancel.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreRefundCancel = new StoreRefundCancel();
StoreRefundCancel.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 StoreRefundCancel:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "30587",
'key': "2b848830072c0db6273d5637ad4b8337",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/refundcancel'
}
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)
StoreRefundCancel = StoreRefundCancel()
StoreRefundCancel.run()
回傳 JSON 結構如下:
{
"key": "2b848830072c0db6273d5637ad4b8337",
"uid": "30587",
"code": "B500",
"msg": "無法取消此筆退款,原因為目前此筆狀態不是申請中。",
"row_data": null
}
特約商店『取消退款』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/refundcancel"} JSON格式,AES256加密資料 |
encry_data | text | 『取消退款』欄位參考 JSON格式,AES256加密資料 |
『取消退款』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店編號(金鑰中心會驗證) | |
key | string | 特約商店驗證碼 (store_token) | |
uid | string | 訂單編號(UID) | |
rows | array | 資料若多筆,則以此欄位判別 |
『取消退款』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
key | string | 特約商店驗證碼 (store_token) | |
uid | string | 訂單編號(UID) | |
code | string | 處理狀態 B200 或 B500 | |
msg | string | 回傳訊息 | |
row_data | object | 退款資訊(即時退款才有此資訊) | 『退款完成回傳資訊』欄位參考 |
電子發票開立
發動交易時一起發動(請參閱PayPage金流交易 交易完成後獨立發動 本節說明獨立開立電子發票作法,交易完成後,可透過MYPAY再發動開立電子發票。電子發票必須在交易後當下立即發動開立,若因系統或服務當下無法開立發票,仍須在48小時內開立完畢。
<?php
/**
* 特約商店串接-直接交易-電子發票開立
*/
final class StoreIssueInvoice
{
/**
* 特約商店商務代號
* @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['uid'] = "69675";
$rawData['key'] = "19436de933c08437720888b1cccf9d0c";
$rawData['invoice_input_type'] = 1;
$rawData['invoice_cloud_type'] = 2;
$rawData['invoice_mobile_code'] = '/NFVIAZP';
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/issueinvoice'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreIssueInvoice = new StoreIssueInvoice();
$StoreIssueInvoice->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 StoreIssueInvoice {
/// <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() {
StoreIssueInvoice simulator = new StoreIssueInvoice();
//僅限走https的Tls 1.2以上版本
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
//發送至遠端
var result = simulator.Post(simulator.GetPostData());
System.Console.WriteLine(result);
}
/// <summary>
/// 取得串接欄位資料
/// </summary>
private dynamic GetRawData() {
dynamic rawData = new ExpandoObject();
rawData.store_uid = this.storeUid;
rawData.uid = "58793";
rawData.key = "ab106dd223c66fd9258454c26e188f01";
rawData.invoice_input_type = 1;
rawData.invoice_cloud_type = 2;
rawData.invoice_mobile_code = "/NFVIAZP";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/issueinvoice";
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 StoreIssueInvoice {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreIssueInvoice simulator = new StoreIssueInvoice();
String json = simulator.post(simulator.getPostData());
System.out.print(json);
}
@SuppressWarnings(value = { "unchecked", "deprecation" })
/**
* 取得串接欄位資料
* @return 串接原始資料
*/
public Map getRawData() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("store_uid", this.storeUid);
rawData.put("uid", "58793");
rawData.put("key", "ab106dd223c66fd9258454c26e188f01");
rawData.put("invoice_input_type", 1);
rawData.put("invoice_cloud_type", 2);
rawData.put("invoice_mobile_code", "/NFVIAZP");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/issueinvoice");
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 StoreIssueInvoice() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreIssueInvoice.prototype.getRawData = function () {
return {
store_uid: this.storeUid,
uid: "58793",
key: "ab106dd223c66fd9258454c26e188f01",
invoice_input_type: 1,
invoice_cloud_type: 2,
invoice_mobile_code: "/NFVIAZP",
};
};
/**
* 取得服務位置
*/
StoreIssueInvoice.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/issueinvoice"
};
};
/**
* AES 256 加密
*/
StoreIssueInvoice.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 到主機
*/
StoreIssueInvoice.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();
});
};
/**
* 取得送出欄位資料
*/
StoreIssueInvoice.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreIssueInvoice.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreIssueInvoice = new StoreIssueInvoice();
StoreIssueInvoice.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 StoreIssueInvoice:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'store_uid': self.storeUid,
'uid': "58793",
'key': "ab106dd223c66fd9258454c26e188f01",
'invoice_input_type': 1,
'invoice_cloud_type': 2,
'invoice_mobile_code': "/NFVIAZP",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/issueinvoice'
}
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)
StoreIssueInvoice = StoreIssueInvoice()
StoreIssueInvoice.run()
回傳 JSON 結構如下:
{
"uid": "107208",
"key": "4b67dabc483338a981d2c3c30a381172",
"code": "B200",
"msg": "發票處理完成",
"order_id": "1234567890",
"actual_cost": 10,
"actual_currency": "TWD",
"state": 2,
"date": "20220427160043",
"wordtrack": "JH",
"number": "00000242",
"rand_code": "3385",
"seller_ban": "28915188",
"buyer_ban": "",
"left_qrcode": "JH00000242111042733850000000a0000000a0000000028915188AzucYuoKTbrLpvBKlKWNMA==:**********:1:1:1:商品名稱:1:10:",
"middle_barcode": "11104JH000002423385",
"right_qrcode": "**",
"title_type": 1,
"title": "123",
"sales_amount": "10",
"tax_amount": "0",
"order_detail": "[{\"Description\":\"商品名稱\",\"Quantity\":\"1\",\"UnitPrice\":\"10\",\"Amount\":\"10\"}]",
"ratetype": 1,
"input_type": 1,
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
特約商店『電子發票開立』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/issueinvoice"} JSON格式,AES256加密資料 |
encry_data | text | 『電子發票開立』欄位參考 JSON格式,AES256加密資料 |
『電子發票開立』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
store_uid | string | 特約商店編號(金鑰中心會驗證) | |
key | string | 特約商店驗證碼 (store_token) | |
uid | string | 訂單編號(UID) | |
invoice_input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
invoice_cloud_type | string | 「雲端發票」類型 | 當invoice_input_type為1,此狀態才有效 『雲端發票類型』值參考 |
invoice_tax_id | string | 統一編號 | 當invoice_input_type為1,此欄位才有效,非必要 |
invoice_mobile_code | string | 手機條碼 | 當invoice_cloud_type為2,此欄位才有效 |
invoice_natural_person | string | 自然人憑證條碼 | 當invoice_cloud_type為3,此欄位才有效 |
invoice_love_code | string | 愛心碼 | 當invoice_input_type為2,此欄位才有效 |
invoice_b2b_title | string | 發票抬頭 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_id | string | 統一編號 | 當invoice_input_type為3時,此欄位才有效 |
invoice_b2b_post_zone | string | 發票郵遞區號 | 當invoice_input_type為3時,此欄位才有效,非必須 |
invoice_b2b_address | string | 發票地址 | 當invoice_input_type為3時,此欄位才有效 |
『電子發票開立』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | 原MYPAYLINK 之交易流水號 | |
key | string | 交易驗証碼 | |
code | string | 主要交易回傳碼(retcode) | |
msg | string | 主要交易回傳碼(retcode) | |
order_id | string | 貴特店系統的訂單編號 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
state | string | 發票開立狀態 0.不處理(預設) 1等候處理中,2發票處理成功 3.發票處理失敗 4.作癈 |
|
state_msg | string | 發票開立狀態訊息 | |
date | string | 發票開立日期(YYYYMMDD) | |
wordtrack | string | 發票字軌 | |
number | string | 發票號碼 | |
rand_code | string | 隨機碼 | |
seller_ban | string | 賣方統編 | |
buyer_ban | string | 買方統編 | |
left_qrcode | string | 左邊QrCode(電子發票查詢碼) | |
middle_barcode | string | 中間Barcode (Code-39格式) | |
right_qrcode | string | 右邊QrCode(電子發票產品資訊-精簡版) | |
title_type | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
title | string | 電子發票標題內容 | |
amount | string | 電子發票開立總額 | |
sales_amount | string | 電子發票銷售額 | |
tax_amount | string | 電子發票稅額 | |
order_detail | string | 電子發票開立之產品詳細資訊(JSON格式) | 『商品細項』值參考 |
ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
電子發票查詢
查詢目前電子發票即時的開立狀態,而交易查詢則包含訂單完整資訊。您可以依您需求作個別查詢。符合查詢規則時,則會回傳相關交易欄位,不符合查詢條件則回傳原查詢欄位。須符合條件才會回傳正確的查詢結果,交易查詢除了提供單筆查詢,也提供一次多筆查詢。
<?php
/**
* 特約商店串接-電子發票查詢
*/
final class StoreInvoice
{
/**
* 特約商店商務代號
* @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['uid'] = "34799";
$rawData['key'] = "c23dd140aa9536eb4c09ef053171110f";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/invoice'
);
}
/**
* AES 256 加密
* @param array $fields
* @param string $key
* @return string
*/
public function encrypt($fields, $key)
{
$data = json_encode($fields);
$size = openssl_cipher_iv_length('AES-256-CBC');
$iv = openssl_random_pseudo_bytes($size);
$data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
$data = base64_encode($iv . $data);
return $data;
}
/**
* 資料 POST 到主機
* @param array $postData
* @return mixed
*/
public function post($postData = [])
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* 取得送出欄位資料
* @return array
*/
public function getPostData ()
{
$postData = array();
$postData['store_uid'] = $this->storeUid;
$postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
$postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
return $postData;
}
/**
* 執行
*/
public function run()
{
$json = $this->post($this->getPostData());
echo $json;
}
}
$StoreInvoice = new StoreInvoice();
$StoreInvoice->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 StoreInvoice {
/// <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() {
StoreInvoice simulator = new StoreInvoice();
//僅限走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.uid = "59665";
rawData.key = "c011e403fa73b088ca3d50aacfa9c43a";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/invoice";
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 StoreInvoice {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreInvoice simulator = new StoreInvoice();
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("uid", "59665");
rawData.put("key", "c011e403fa73b088ca3d50aacfa9c43a");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/invoice");
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 StoreInvoice() {
// 特約商店商務代號
this.storeUid = "289151880002";
// 特約商店金鑰或認證碼
this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
// 串接交易位置
this.url = "https://pay.usecase.cc/api/init";
};
/**
* 取得串接欄位資料
*/
StoreInvoice.prototype.getRawData = function () {
return {
uid: "59665",
key: "c011e403fa73b088ca3d50aacfa9c43a",
};
};
/**
* 取得服務位置
*/
StoreInvoice.prototype.getService = function () {
return {
service_name: "api",
cmd: "api/invoice"
};
};
/**
* AES 256 加密
*/
StoreInvoice.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 到主機
*/
StoreInvoice.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();
});
};
/**
* 取得送出欄位資料
*/
StoreInvoice.prototype.getPostData = function () {
return {
"store_uid": this.storeUid,
"service": this.encrypt(this.getService(), this.storeKey),
"encry_data": this.encrypt(this.getRawData(), this.storeKey)
};
};
/**
* 執行
*/
StoreInvoice.prototype.run = async function () {
json = await this.post(this.getPostData())
console.log(json);
};
StoreInvoice = new StoreInvoice();
StoreInvoice.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 StoreInvoice:
# 特約商店商務代號
storeUid = "289151880002"
# 特約商店金鑰或認證碼
storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
# 串接交易位置
url = "https://pay.usecase.cc/api/init"
def getRawData(self):
"""取得串接欄位資料
Returns:
{dict}: 欄位資料
"""
rawData = {
'uid': "59665",
'key': "c011e403fa73b088ca3d50aacfa9c43a",
}
return rawData
def getService(self):
"""取得服務位置
Returns:
{dict}: 服務位置資料
"""
return {
'service_name': 'api',
'cmd': 'api/invoice'
}
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)
StoreInvoice = StoreInvoice()
StoreInvoice.run()
回傳 JSON 結構如下:
{
"uid": 34799,
"key": "c23dd140aa9536eb4c09ef053171110f",
"prc": "250",
"order_id": "1234567890",
"cost": 1,
"currency": "TWD",
"actual_cost": 1,
"actual_currency": "TWD",
"state": 2,
"date": "20181101100330",
"wordtrack": "AA",
"number": "12345678",
"rand_code": "",
"seller_ban": "",
"buyer_ban": "",
"left_qrcode": "",
"middle_barcode": "",
"right_qrcode": "",
"title_type": 1,
"title": "",
"print_type": 0,
"print_device": 0,
"amount": "0",
"sales_amount": "0",
"tax_amount": "0",
"order_detail": "[]",
"ratetype": 1,
"input_type": 2,
"cloud_type": "",
"mobile_code": "",
"tax_id": "",
"natural_person": "",
"m_post_zone": "",
"m_address": "",
"love_code": "241",
"b2b_title": "",
"b2b_id": "",
"b2b_post_zone": "",
"b2b_address": "",
"allowance": [],
"group_id": "",
"payment_name": "",
"nois": "",
"echo_0": "",
"echo_1": "",
"echo_2": "",
"echo_3": "",
"echo_4": ""
}
特約商店『電子發票查詢』參數說明
欄位 | 型態 | 說明 |
---|---|---|
store_uid | string(16) | 特約商店商務代號 |
service | text | {"service_name": "api", "cmd": "api\/invoice"} JSON格式,AES256加密資料 |
encry_data | text | 『電子發票查詢』欄位參考 JSON格式,AES256加密資料 |
『電子發票查詢』欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | 訂單 uid | |
key | string | 查詢驗證碼 |
『電子發票查詢』回傳欄位
參數名稱 | 型態 | 說明 | 必須 |
---|---|---|---|
uid | string | 原MYPAYLINK 之交易流水號 | |
key | string | 交易驗証碼 | |
prc | string | 主要交易回傳碼(retcode) | |
order_id | string | 貴特店系統的訂單編號 | |
cost | string | 交易金額 | |
currency | string | 交易幣別 | |
actual_cost | string | 實際交易金額 | |
actual_currency | string | 實際交易幣別 | |
state | string | 發票開立狀態 0.不處理(預設) 1等候處理中,2發票處理成功 3.發票處理失敗 4.作癈 5.系統服務異常 6.折讓 |
|
date | string | 發票開立日期(YYYYMMDD) | |
wordtrack | string | 發票字軌 | |
number | string | 發票號碼 | |
rand_code | string | 隨機碼 | |
seller_ban | string | 賣方統編 | |
buyer_ban | string | 買方統編 | |
left_qrcode | string | 左邊QrCode(電子發票查詢碼) | |
middle_barcode | string | 中間Barcode (Code-39格式) | |
right_qrcode | string | 右邊QrCode(電子發票產品資訊-精簡版) | |
title_type | integer | 電子發票列印標題格式 | 『電子發票紙本列印標題類型』值參考 |
title | string | 電子發票標題內容 | |
print_type | integer | 電子發票列印類型 | 『電子發票列印類型』值參考 |
print_device | integer | 電子發票列印設備 | 『電子發票列印設備』值參考 |
amount | string | 電子發票開立總額 | |
sales_amount | string | 電子發票銷售額 | |
tax_amount | string | 電子發票稅額 | |
order_detail | string | 電子發票開立之產品詳細資訊(JSON格式) | 『商品細項』值參考 |
ratetype | integer | 電子發票稅率別 | 『電子發票稅率別』值參考 |
input_type | integer | 電子發票開立類型 | 『電子發票開立類型』值參考 |
cloud_type | string | 電子發票開立類型-雲端發票類型 | 『雲端發票類型』值參考 |
mobile_code | string | 當cloud_type為2時紀錄的手機條碼 | |
tax_id | string | 當cloud_type為2時紀錄的統一編號 | |
natural_person | string | 當cloud_type為3時紀錄的自然人憑證條碼 | |
m_post_zone | string | 當cloud_type為4時紀錄中獎時紙本發票郵遞區號 | |
m_address | string | 當cloud_type為4時紀錄中獎時紙本發票收件住址 | |
love_code | string | 當input_type為2時紀錄的愛心碼 | |
b2b_title | string | 當input_type為3時紀錄的發票抬頭 | |
b2b_id | string | 當input_type為3時紀錄的統一編號 | |
b2b_post_zone | string | 當input_type為3時紀錄的郵遞區號 | |
b2b_address | string | 當input_type為3時紀錄的發票地址 | |
allowance | array | 電子發票折讓資訊 | 每筆『電子發票折讓資訊』欄位參考 |
group_id | string | 1.定期定額式扣款編號 2.定期分期式扣款編號 |
|
payment_name | string | 定期定額式/定期分期式扣款名稱 | |
nois | string | 定期定額式/定期分期式扣繳期數 | |
echo_0 | string | 自訂回傳參數 1 | |
echo_1 | string | 自訂回傳參數 2 | |
echo_2 | string | 自訂回傳參數 3 | |
echo_3 | string | 自訂回傳參數 4 | |
echo_4 | string | 自訂回傳參數 5 |
特約商店可用支付工具查詢
<?php
/**
* 特約商店串接-查詢特約商店支援之支付方式
*/
final class StoreSupportPayMode
{
/**
* 特約商店商務代號
* @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['cost'] = "1000";
$rawData['currency'] = "TWD";
return $rawData;
}
/**
* 取得服務位置
* @return array
*/
public function getService()
{
return array(
'service_name' => 'api',
'cmd' => 'api/paymentsupportpaymode'
);
}
/**
* 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;
}
}
$StoreSupportPayMode = new StoreSupportPayMode();
$StoreSupportPayMode->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 StoreSupportPayMode {
/// <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() {
StoreSupportPayMode simulator = new StoreSupportPayMode();
//僅限走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.cost = "1000";
rawData.currency = "TWD";
return rawData;
}
/// <summary>
/// 取得服務位置
/// </summary>
private ServiceRequest GetService() {
ServiceRequest rawData = new ServiceRequest();
rawData.service_name = "api";
rawData.cmd = "api/paymentsupportpaymode";
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 StoreSupportPayMode {
/**
* 特約商店商務代號
*/
String storeUid = "289151880002";
/**
* 特約商店金鑰或認證碼
*/
String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
/**
* 串接交易位置
*/
String url = "https://pay.usecase.cc/api/init";
/**
* 執行
* @param args
*/
public static void main(String[] args) {
StoreSupportPayMode simulator = new StoreSupportPayMode();
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("cost", "1000");
rawData.put("currency", "TWD");
return rawData;
}
/**
* 取得服務位置
* @return 串接服務資料
*/
public Map getService() {
Map<Object, Object> rawData = new HashMap<Object, Object>();
rawData.put("service_name", "api");
rawData.put("cmd", "api/paymentsupportpaymode");
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)