NAV
php csharp java javascript python

修改歷程

版本 異動日期 修訂內容 位置
1.0 2021.01.01 新版文件
1.0.1 2023.04.07 新增開發前請先閱讀 20230407_01

開發前請先閱讀

金鑰的注意事項

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,系統會寄發信件給予商家的技術人員以及附錄三而外設定的人員

票券交易設計概要

安全性設計

所有的要求發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES加密,再透過HTTPS加密傳輸。交易資料不由消費者端送出,可確保資料不被消費者竄改。所有付費資訊經過MYPAY Link匝道進行轉送處理,特約商店不需要處理消費者的付費流程以及安全控管。

MYTIX 目前提供之服務與格式

MYTIX 票券交易應用情境如下:

(1)購買票券交易:透過MYTIX發動購買票券,經由MYPAY交易完成後,通知特約商店購買之票券是否完成交易。

(2)購買票券交易查詢:查詢購買票券訂單結果。

(3)票券核銷交易:已完成付費的票券,可發動票券核銷。

(4)票券核銷查詢:查詢票券核銷訂單核銷結果。

(5)票券退款交易:發動對未使用的票券進行退款。

(6)票券退款交易查詢:查詢票券退款交易之結果。

我們提供介接方式是透過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相容編碼

購買票券交易

<?php
/**
 * 特約商店串接-購買票券交易
 */
final class StoreVoucher
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @var string
     */
    public $url = "https://pay.usecase.cc/api/init";
    /**
     * 取得串接欄位資料
     * @return array
     */
    public function getRawData()
    {
        $rawData = array();
        $rawData['store_uid'] = $this->storeUid;
        $rawData['user_data'] = [
                                 'user_id' => 'phper',
                                 'user_name' => '金城武',
                                 'user_real_name' => '金城武',
                                 'ip' => '127.0.0.1'
                                ];
        $rawData['order_id'] = "VS20210917113800";
        $rawData['pfn'] = "0";
        $rawData['cost'] = 100;
        $rawData['currency'] = "TWD";
        $rawData['items'] = [
                                [
                                 'id' => 'H01',
                                 'name' => '用餐券',
                                 'amount' => 2,
                                 'price' => 50,
                                 'cost' => 50,
                                 'total_price' => 100,
                                 'total_cost' => 100,
                                 'gift_number' => 0,
                                 'trust_start_date' => '20210917',
                                 'trust_end_date' => '20220916',
                                 'is_custom_serial' => 1,
                                 'serial_numbers' => '["A00049","A00050"]',
                                 'gift_serial_numbers' => '[]',
                                 'sharing' => 
                                    [
                                     'headquarters' => '0.15',
                                     'sales' => '0.1',
                                     'reimbursement' => '0.75'
                                    ]
                                ]
                            ];
        $rawData['declarations'] = [
                                       [
                                        'name' => '用餐券',
                                        'content' => '<h6>使用需知</h6>
                                <ul>
                                <li>本行程入住花蓮藍天麗池飯店或同等級。</li>
                                <li>本行程為套裝行程,旅客須完成住宿登記並於線上付款或當天於易遊網門市、ATM 付款完畢後,座位始予保留。</li>
                                <li>票價以套裝行程發售,價格依房型而異(發售指定區間乘車票,旅客可擇當次車指定上下車站上下車,不需另補差額,餘程亦不退費)。
                                </li>
                                <li>成人票:4,999 元/人起,孩童及敬老票另有優惠。</li>
                                <li>套票價格及詳細資訊請參閱易遊網郵輪式列車網頁(請點檢視行程)。</li></ul>'
                                       ]
                                   ];

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/voucherservicepurchase'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucher = new StoreVoucher();
$StoreVoucher->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 StoreVoucher {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucher simulator = new StoreVoucher();
            //僅限走https的Tls 1.2以上版本
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            //發送至遠端
            var result = simulator.Post(simulator.GetPostData());

            System.Console.WriteLine(result);
        }
        /// <summary>
        /// 取得串接欄位資料
        /// </summary>
        private dynamic GetRawData() {

            dynamic userData = new ExpandoObject();
            userData.user_id = "phper";
            userData.user_name = "金城武";
            userData.user_real_name = "金城武";
            userData.ip = "127.0.0.1";

            ArrayList items = new ArrayList();

            dynamic item = new ExpandoObject();
            item.id = "H01";
            item.name = "用餐券";
            item.amount = 2;
            item.price = 50;
            item.cost = 50;
            item.total_price = 100;
            item.total_cost = 100;
            item.gift_number = 0;
            item.trust_start_date = "20210917";
            item.trust_end_date = "20220916";
            item.is_custom_serial = 1;
            item.serial_numbers = "[\"A00049\",\"A00050\"]";
            item.gift_serial_numbers = "[]";

            dynamic sharing = new ExpandoObject();
            sharing.headquarters = "0.15";
            sharing.sales = "0.1";
            sharing.reimbursement = "0.75";
            item.sharing = sharing;
            items.Add(item);


            ArrayList declarations = new ArrayList();

            dynamic declaration = new ExpandoObject();
            declaration.name = "用餐券";
            declaration.content = "<h6>使用需知</h6>\r\n                                <ul>\r\n                                <li>本行程入住花蓮藍天麗池飯店或同等級。</li>\r\n                                <li>本行程為套裝行程,旅客須完成住宿登記並於線上付款或當天於易遊網門市、ATM 付款完畢後,座位始予保留。</li>\r\n                                <li>票價以套裝行程發售,價格依房型而異(發售指定區間乘車票,旅客可擇當次車指定上下車站上下車,不需另補差額,餘程亦不退費)。\r\n                                </li>\r\n                                <li>成人票:4,999 元/人起,孩童及敬老票另有優惠。</li>\r\n                                <li>套票價格及詳細資訊請參閱易遊網郵輪式列車網頁(請點檢視行程)。</li></ul>";
            declarations.Add(declaration);


            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.user_data = userData;
            rawData.order_id =  "VS20210917113800";
            rawData.pfn =  "0";
            rawData.cost =  100;
            rawData.currency =  "TWD";
            rawData.items = items;
            rawData.declarations = declarations;

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/voucherservicepurchase";
            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 StoreVoucher {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucher simulator = new StoreVoucher();
        String json = simulator.post(simulator.getPostData());
        System.out.print(json);
    }

    @SuppressWarnings(value = { "unchecked", "deprecation" })
    /**
     * 取得串接欄位資料
     * @return 串接原始資料
     */
    public Map getRawData() {

        Map<Object, Object> userData = new HashMap<Object, Object>();
        userData.put("user_id", "phper");
        userData.put("user_name", "金城武");
        userData.put("user_real_name", "金城武");
        userData.put("ip", "127.0.0.1");

        ArrayList items = new ArrayList();

        Map<Object, Object> item = new HashMap<Object, Object>();
        item.put("id", "H01");
        item.put("name", "用餐券");
        item.put("amount", 2);
        item.put("price", 50);
        item.put("cost", 50);
        item.put("total_price", 100);
        item.put("total_cost", 100);
        item.put("gift_number", 0);
        item.put("trust_start_date", "20210917");
        item.put("trust_end_date", "20220916");
        item.put("is_custom_serial", 1);
        item.put("serial_numbers", "[\"A00049\",\"A00050\"]");
        item.put("gift_serial_numbers", "[]");

        Map<Object, Object> sharing = new HashMap<Object, Object>();
        sharing.put("headquarters", "0.15");
        sharing.put("sales", "0.1");
        sharing.put("reimbursement", "0.75");
        item.put("sharing", sharing);
        items.add(item);


        ArrayList declarations = new ArrayList();

        Map<Object, Object> declaration = new HashMap<Object, Object>();
        declaration.put("name", "用餐券");
        declaration.put("content", "<h6>使用需知</h6>\r\n                                <ul>\r\n                                <li>本行程入住花蓮藍天麗池飯店或同等級。</li>\r\n                                <li>本行程為套裝行程,旅客須完成住宿登記並於線上付款或當天於易遊網門市、ATM 付款完畢後,座位始予保留。</li>\r\n                                <li>票價以套裝行程發售,價格依房型而異(發售指定區間乘車票,旅客可擇當次車指定上下車站上下車,不需另補差額,餘程亦不退費)。\r\n                                </li>\r\n                                <li>成人票:4,999 元/人起,孩童及敬老票另有優惠。</li>\r\n                                <li>套票價格及詳細資訊請參閱易遊網郵輪式列車網頁(請點檢視行程)。</li></ul>");
        declarations.add(declaration);


        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("store_uid", this.storeUid);
        rawData.put("user_data", userData);
        rawData.put("order_id", "VS20210917113800");
        rawData.put("pfn", "0");
        rawData.put("cost", 100);
        rawData.put("currency", "TWD");
        rawData.put("items", items);
        rawData.put("declarations", declarations);

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/voucherservicepurchase");
        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 StoreVoucher() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucher.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        user_data: {
                    'user_id': "phper",
                    'user_name': "金城武",
                    'user_real_name': "金城武",
                    'ip': "127.0.0.1"
                   },
        order_id: "VS20210917113800",
        pfn: "0",
        cost: 100,
        currency: "TWD",
        items: [
                   {
                    'id': "H01",
                    'name': "用餐券",
                    'amount': 2,
                    'price': 50,
                    'cost': 50,
                    'total_price': 100,
                    'total_cost': 100,
                    'gift_number': 0,
                    'trust_start_date': "20210917",
                    'trust_end_date': "20220916",
                    'is_custom_serial': 1,
                    'serial_numbers': "[\"A00049\",\"A00050\"]",
                    'gift_serial_numbers': "[]",
                    'sharing': 
                       {
                        'headquarters': "0.15",
                        'sales': "0.1",
                        'reimbursement': "0.75"
                       }
                   }
               ],
        declarations: [
                          {
                           'name': "用餐券",
                           'content': "<h6>使用需知</h6>\r\n                                <ul>\r\n                                <li>本行程入住花蓮藍天麗池飯店或同等級。</li>\r\n                                <li>本行程為套裝行程,旅客須完成住宿登記並於線上付款或當天於易遊網門市、ATM 付款完畢後,座位始予保留。</li>\r\n                                <li>票價以套裝行程發售,價格依房型而異(發售指定區間乘車票,旅客可擇當次車指定上下車站上下車,不需另補差額,餘程亦不退費)。\r\n                                </li>\r\n                                <li>成人票:4,999 元/人起,孩童及敬老票另有優惠。</li>\r\n                                <li>套票價格及詳細資訊請參閱易遊網郵輪式列車網頁(請點檢視行程)。</li></ul>"
                          }
                      ],

    };
};
/**
 * 取得服務位置
 */
StoreVoucher.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/voucherservicepurchase"
    };
};
/**
 * AES 256 加密
 */
StoreVoucher.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 到主機
 */
StoreVoucher.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucher.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucher.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucher = new StoreVoucher();
StoreVoucher.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 StoreVoucher:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'user_data': {
                          'user_id': "phper",
                          'user_name': "金城武",
                          'user_real_name': "金城武",
                          'ip': "127.0.0.1"
                         },
            'order_id': "VS20210917113800",
            'pfn': "0",
            'cost': 100,
            'currency': "TWD",
            'items': [
                         {
                          'id': "H01",
                          'name': "用餐券",
                          'amount': 2,
                          'price': 50,
                          'cost': 50,
                          'total_price': 100,
                          'total_cost': 100,
                          'gift_number': 0,
                          'trust_start_date': "20210917",
                          'trust_end_date': "20220916",
                          'is_custom_serial': 1,
                          'serial_numbers': "[\"A00049\",\"A00050\"]",
                          'gift_serial_numbers': "[]",
                          'sharing': 
                             {
                              'headquarters': "0.15",
                              'sales': "0.1",
                              'reimbursement': "0.75"
                             }
                         }
                     ],
            'declarations': [
                                {
                                 'name': "用餐券",
                                 'content': "<h6>使用需知</h6>\r\n                                <ul>\r\n                                <li>本行程入住花蓮藍天麗池飯店或同等級。</li>\r\n                                <li>本行程為套裝行程,旅客須完成住宿登記並於線上付款或當天於易遊網門市、ATM 付款完畢後,座位始予保留。</li>\r\n                                <li>票價以套裝行程發售,價格依房型而異(發售指定區間乘車票,旅客可擇當次車指定上下車站上下車,不需另補差額,餘程亦不退費)。\r\n                                </li>\r\n                                <li>成人票:4,999 元/人起,孩童及敬老票另有優惠。</li>\r\n                                <li>套票價格及詳細資訊請參閱易遊網郵輪式列車網頁(請點檢視行程)。</li></ul>"
                                }
                            ],
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/voucherservicepurchase'
        }

    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)

StoreVoucher = StoreVoucher()
StoreVoucher.run()

回傳 JSON 結構如下:

{
    "code": "200",
    "msg": "資料正確",
    "uid": "93",
    "key": "dd30e98b0835f90ef8a9ba5f25ac29dd23712f94",
    "url": "https:\/\/pay.usecase.cc\/voucher\/93.html"
}

透過MYTIX發動購買票券,當消費者交易完成後,將交易結果通知特約商店。當特約商店確認交易成功後,購買之票券則可透過核銷來做消費。

特約商店『購買票券交易』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/voucherservicepurchase"}
JSON格式,AES256加密資料
encry_data text 『購買票券交易』欄位參考
JSON格式,AES256加密資料

『購買票券交易』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店訂單編號(唯一) 必填
pfn string 指定購買票券時,指定支付方式(不指定請帶0) 必填
user_data object 消費者資訊 必填
『消費者資訊』欄位參考
service_type integer 票券服務類型 『票券服務類型』值參考
area_type integer 適用區域類型 『適用區域類型』值參考
is_pre_issue integer 是否為預發行 『是否為預發行』值參考
cost string 訂單總金額 必填
currency string 預設交易幣別(預設為TWD新台幣)
items array 訂單物品資訊 必填
每筆『票券項目』欄位參考
declarations array 票券的宣告
票券宣告與商品資訊票券種類的數量與名稱必須相同
必填
每筆『票券宣告』欄位參考
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 購買交易失敗導頁網址

『購買票券交易』回傳欄位

參數名稱 型態 說明 必須
code string 交易回傳碼 『購買票券交易狀態碼』值參考
msg string 回傳訊息
uid string 購買票券交易訂單UID
key string 交易驗証碼
url string 交易網址

『購買票券交易回傳資訊』回報欄位

參數名稱 型態 說明 必須
uid string 購買票券交易訂單UID
key string 交易驗証碼
code string 目前狀態碼 『購買票券交易狀態碼』值參考
msg string 回傳訊息
finishtime string 交易完成時間(YYYYMMDDHHmmss)
order_id string 貴特店系統的訂單編號
user_id string 消費者帳號
cost string 原交易金額
currency string 原交易幣別
actual_cost string 實際交易金額
actual_currency string 實際交易幣別
pfn string 付費方法
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

購買票券交易查詢

<?php
/**
 * 特約商店串接-購買票券交易查詢
 */
final class StoreVoucherQuery
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @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'] = "VS20210908120309";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/voucherservicequery'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucherQuery = new StoreVoucherQuery();
$StoreVoucherQuery->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 StoreVoucherQuery {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucherQuery simulator = new StoreVoucherQuery();
            //僅限走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 =  "VS20210908120309";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/voucherservicequery";
            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 StoreVoucherQuery {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucherQuery simulator = new StoreVoucherQuery();
        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", "VS20210908120309");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/voucherservicequery");
        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 StoreVoucherQuery() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucherQuery.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        order_id: "VS20210908120309",

    };
};
/**
 * 取得服務位置
 */
StoreVoucherQuery.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/voucherservicequery"
    };
};
/**
 * AES 256 加密
 */
StoreVoucherQuery.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 到主機
 */
StoreVoucherQuery.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucherQuery.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucherQuery.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucherQuery = new StoreVoucherQuery();
StoreVoucherQuery.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 StoreVoucherQuery:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'order_id': "VS20210908120309",
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/voucherservicequery'
        }

    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)

StoreVoucherQuery = StoreVoucherQuery()
StoreVoucherQuery.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "content": {
        "uid": "86",
        "key": "3caf6a482f79f1dd6ed16448836c7a16",
        "code": "250",
        "msg": "付款完成",
        "finishtime": "20210916164212",
        "order_id": "VS20210908120309",
        "user_id": "phper",
        "cost": "100",
        "currency": "TWD",
        "actual_cost": "100",
        "actual_currency": "TWD",
        "pfn": "CREDITCARD",
        "echo_0": "",
        "echo_1": "",
        "echo_2": "",
        "echo_3": "",
        "echo_4": ""
    }
}

發動購買票券交易後,可透過此方法查詢購買票券訂單交易結果。

特約商店『購買票券交易查詢』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/voucherservicequery"}
JSON格式,AES256加密資料
encry_data text 『購買票券交易查詢』欄位參考
JSON格式,AES256加密資料

『購買票券交易查詢』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店訂單編號 必填

『購買票券交易查詢』回傳欄位

參數名稱 型態 說明 必須
code string 購買交易狀態碼
msg string 回傳訊息
content object 『購買票券交易查詢回傳』欄位參考

『購買票券交易查詢回傳』欄位

參數名稱 型態 說明 必須
uid string 購買票券交易訂單UID
key string 交易驗証碼
code string 目前狀態碼 『購買票券交易狀態碼』值參考
msg string 回傳訊息
finishtime string 交易完成時間(YYYYMMDDHHmmss)
order_id string 貴特店系統的訂單編號
user_id string 消費者帳號
cost string 原交易金額
currency string 原交易幣別
actual_cost string 實際交易金額
actual_currency string 實際交易幣別
pfn string 付費方法
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

票券核銷交易

<?php
/**
 * 特約商店串接-票券核銷交易
 */
final class StoreVoucherTransaction
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @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'] = "VT20210917114023";
        $rawData['user_id'] = "phper";
        $rawData['ip'] = "127.0.0.1";
        $rawData['items'] = [
                                [
                                 'id' => 'H01',
                                 'serial_number' => 'A00021'
                                ],
                                [
                                 'id' => 'H01',
                                 'serial_number' => 'A00022'
                                ]
                            ];
        $rawData['echo_0'] = "";
        $rawData['echo_1'] = "";
        $rawData['echo_2'] = "";
        $rawData['echo_3'] = "";
        $rawData['echo_4'] = "";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/vouchertransaction'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucherTransaction = new StoreVoucherTransaction();
$StoreVoucherTransaction->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 StoreVoucherTransaction {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucherTransaction simulator = new StoreVoucherTransaction();
            //僅限走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 item1 = new ExpandoObject();
            item1.id = "H01";
            item1.serial_number = "A00021";
            items.Add(item1);

            dynamic item2 = new ExpandoObject();
            item2.id = "H01";
            item2.serial_number = "A00022";
            items.Add(item2);


            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.order_id =  "VT20210917114023";
            rawData.user_id =  "phper";
            rawData.ip =  "127.0.0.1";
            rawData.items = items;
            rawData.echo_0 =  "";
            rawData.echo_1 =  "";
            rawData.echo_2 =  "";
            rawData.echo_3 =  "";
            rawData.echo_4 =  "";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/vouchertransaction";
            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 StoreVoucherTransaction {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucherTransaction simulator = new StoreVoucherTransaction();
        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> item1 = new HashMap<Object, Object>();
        item1.put("id", "H01");
        item1.put("serial_number", "A00021");
        items.add(item1);

        Map<Object, Object> item2 = new HashMap<Object, Object>();
        item2.put("id", "H01");
        item2.put("serial_number", "A00022");
        items.add(item2);


        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("store_uid", this.storeUid);
        rawData.put("order_id", "VT20210917114023");
        rawData.put("user_id", "phper");
        rawData.put("ip", "127.0.0.1");
        rawData.put("items", items);
        rawData.put("echo_0", "");
        rawData.put("echo_1", "");
        rawData.put("echo_2", "");
        rawData.put("echo_3", "");
        rawData.put("echo_4", "");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/vouchertransaction");
        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 StoreVoucherTransaction() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucherTransaction.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        order_id: "VT20210917114023",
        user_id: "phper",
        ip: "127.0.0.1",
        items: [
                   {
                    'id': "H01",
                    'serial_number': "A00021"
                   },
                   {
                    'id': "H01",
                    'serial_number': "A00022"
                   }
               ],
        echo_0: "",
        echo_1: "",
        echo_2: "",
        echo_3: "",
        echo_4: "",

    };
};
/**
 * 取得服務位置
 */
StoreVoucherTransaction.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/vouchertransaction"
    };
};
/**
 * AES 256 加密
 */
StoreVoucherTransaction.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 到主機
 */
StoreVoucherTransaction.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucherTransaction.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucherTransaction.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucherTransaction = new StoreVoucherTransaction();
StoreVoucherTransaction.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 StoreVoucherTransaction:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'order_id': "VT20210917114023",
            'user_id': "phper",
            'ip': "127.0.0.1",
            'items': [
                         {
                          'id': "H01",
                          'serial_number': "A00021"
                         },
                         {
                          'id': "H01",
                          'serial_number': "A00022"
                         }
                     ],
            'echo_0': "",
            'echo_1': "",
            'echo_2': "",
            'echo_3': "",
            'echo_4': "",
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/vouchertransaction'
        }

    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)

StoreVoucherTransaction = StoreVoucherTransaction()
StoreVoucherTransaction.run()

回傳 JSON 結構如下:

{
    "code": "250",
    "msg": "核銷成功",
    "uid": "120",
    "key": "9c485e5d2c902d915aada18b2ddae3ae",
    "order_id": "VT20210917114023",
    "reimbursement_date": "20210917134815",
    "items": [
        {
            "id": "H01",
            "serial_number": "A00021",
            "is_paid": 1,
            "sharing": {
                "headquarters": 0.15,
                "sales": 0.1,
                "reimbursement": 0.75
            },
            "fee": {
                "cost": 50,
                "gateway_fee": 1,
                "escrow_fee": 1,
                "issuance_layer": 2,
                "issuance_fee": 1,
                "headquarters": 7,
                "sales": 5,
                "reimbursement": 35
            }
        },
        {
            "id": "H01",
            "serial_number": "A00022",
            "is_paid": 1,
            "sharing": {
                "headquarters": 0.15,
                "sales": 0.1,
                "reimbursement": 0.75
            },
            "fee": {
                "cost": 50,
                "gateway_fee": 1,
                "escrow_fee": 1,
                "issuance_layer": 2,
                "issuance_fee": 1,
                "headquarters": 7,
                "sales": 5,
                "reimbursement": 35
            }
        }
    ],
    "echo_0": "",
    "echo_1": "",
    "echo_2": "",
    "echo_3": "",
    "echo_4": ""
}

透過系統做直接票券核銷時,可發動此票券核銷交易。「平台客戶」可使用自訂票券券號做核銷,而「一般客戶」使用MYTIX票券券號做核銷。

特約商店『票券核銷交易』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/vouchertransaction"}
JSON格式,AES256加密資料
encry_data text 『票券核銷交易』欄位參考
JSON格式,AES256加密資料

『票券核銷交易』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店訂單編號 必填
user_id string 消費者帳號 必填
ip string 消費者來源 IP 必填
items array 核銷票券資料 每筆『票券核銷項目(request)』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

『票券核銷交易』回傳欄位

參數名稱 型態 說明 必須
code string 交易回傳碼 『票券核銷交易狀態碼』值參考
msg string 回傳訊息
uid string 票券交易訂單UID
key string 交易驗証碼
order_id string 特約商店訂單編號
reimbursement_date string 核銷成功時間(格式(YYYYMMDDHHmmss))
items array 核銷票券項目 每筆『票券核銷項目(response)』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

『票券核銷交易回傳資訊』回報欄位

參數名稱 型態 說明 必須
order_id string 特店商店的訂單編號
uid string 票券交易訂單UID
key string 驗証碼
code string 票券核銷狀態碼 『票券核銷交易狀態碼』值參考
msg string 回傳訊息
reimbursement_date string 核銷成功時間(格式(YYYYMMDDHHmmss))
items array 核銷票券項目 每筆『票券核銷項目(response)』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

票券核銷查詢

<?php
/**
 * 特約商店串接-票券核銷查詢
 */
final class StoreVoucherTransactionQuery
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @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'] = "VT20210917134815";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/voucherquery'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucherTransactionQuery = new StoreVoucherTransactionQuery();
$StoreVoucherTransactionQuery->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 StoreVoucherTransactionQuery {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucherTransactionQuery simulator = new StoreVoucherTransactionQuery();
            //僅限走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 =  "VT20210917134815";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/voucherquery";
            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 StoreVoucherTransactionQuery {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucherTransactionQuery simulator = new StoreVoucherTransactionQuery();
        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", "VT20210917134815");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/voucherquery");
        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 StoreVoucherTransactionQuery() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucherTransactionQuery.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        order_id: "VT20210917134815",

    };
};
/**
 * 取得服務位置
 */
StoreVoucherTransactionQuery.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/voucherquery"
    };
};
/**
 * AES 256 加密
 */
StoreVoucherTransactionQuery.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 到主機
 */
StoreVoucherTransactionQuery.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucherTransactionQuery.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucherTransactionQuery.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucherTransactionQuery = new StoreVoucherTransactionQuery();
StoreVoucherTransactionQuery.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 StoreVoucherTransactionQuery:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'order_id': "VT20210917134815",
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/voucherquery'
        }

    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)

StoreVoucherTransactionQuery = StoreVoucherTransactionQuery()
StoreVoucherTransactionQuery.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "content": {
        "order_id": "VT20210917134815",
        "uid": "120",
        "key": "9c485e5d2c902d915aada18b2ddae3ae",
        "code": "250",
        "msg": "核銷成功",
        "reimbursement_date": "20210917134815",
        "items": [
            {
                "id": "H01",
                "serial_number": "A00005",
                "is_paid": 1,
                "sharing": {
                    "headquarters": 0.15,
                    "sales": 0.1,
                    "reimbursement": 0.75
                },
                "fee": {
                    "cost": 50,
                    "gateway_fee": 1,
                    "escrow_fee": 1,
                    "issuance_layer": 2,
                    "issuance_fee": 1,
                    "headquarters": 7,
                    "sales": 5,
                    "reimbursement": 35
                }
            },
            {
                "id": "H01",
                "serial_number": "A00006",
                "is_paid": 1,
                "sharing": {
                    "headquarters": 0.15,
                    "sales": 0.1,
                    "reimbursement": 0.75
                },
                "fee": {
                    "cost": 50,
                    "gateway_fee": 1,
                    "escrow_fee": 1,
                    "issuance_layer": 2,
                    "issuance_fee": 1,
                    "headquarters": 7,
                    "sales": 5,
                    "reimbursement": 35
                }
            }
        ],
        "echo_0": "",
        "echo_1": "",
        "echo_2": "",
        "echo_3": "",
        "echo_4": ""
    }
}

當發動票券核銷交易後,若發生網路中斷或重新查詢票券核銷訂單之核銷結果,可發動此查詢確認核銷狀態與結果。

特約商店『票券核銷查詢』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/voucherquery"}
JSON格式,AES256加密資料
encry_data text 『票券核銷查詢』欄位參考
JSON格式,AES256加密資料

『票券核銷查詢』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店訂單編號 必填

『票券核銷查詢』回傳欄位

參數名稱 型態 說明 必須
code string 票券交易狀態碼 『票券核銷交易狀態碼』值參考
msg string 回傳訊息
content object 查詢內容 『票券核銷訂單查詢內容回傳』欄位參考

『票券核銷訂單查詢內容回傳』欄位

參數名稱 型態 說明 必須
order_id string 特店商店的訂單編號
uid string 票券交易訂單UID
key string 驗証碼
code string 狀態碼
msg string 回傳訊息
reimbursement_date string 核銷成功時間(格式(YYYYMMDDHHmmss))
items array 核銷票券項目 每筆『票券核銷項目(response)』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

票券退款交易

<?php
/**
 * 特約商店串接-票券退款交易
 */
final class StoreVoucherRefund
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @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'] = "VR20211001135554";
        $rawData['items'] = [
                                [
                                 'id' => 'H01',
                                 'serial_number' => 'A00052'
                                ],
                                [
                                 'id' => 'H01',
                                 'serial_number' => 'A00053'
                                ]
                            ];
        $rawData['echo_0'] = "";
        $rawData['echo_1'] = "";
        $rawData['echo_2'] = "";
        $rawData['echo_3'] = "";
        $rawData['echo_4'] = "";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/voucherrefund'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucherRefund = new StoreVoucherRefund();
$StoreVoucherRefund->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 StoreVoucherRefund {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucherRefund simulator = new StoreVoucherRefund();
            //僅限走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 item1 = new ExpandoObject();
            item1.id = "H01";
            item1.serial_number = "A00052";
            items.Add(item1);

            dynamic item2 = new ExpandoObject();
            item2.id = "H01";
            item2.serial_number = "A00053";
            items.Add(item2);


            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.order_id =  "VR20211001135554";
            rawData.items = items;
            rawData.echo_0 =  "";
            rawData.echo_1 =  "";
            rawData.echo_2 =  "";
            rawData.echo_3 =  "";
            rawData.echo_4 =  "";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/voucherrefund";
            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 StoreVoucherRefund {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucherRefund simulator = new StoreVoucherRefund();
        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> item1 = new HashMap<Object, Object>();
        item1.put("id", "H01");
        item1.put("serial_number", "A00052");
        items.add(item1);

        Map<Object, Object> item2 = new HashMap<Object, Object>();
        item2.put("id", "H01");
        item2.put("serial_number", "A00053");
        items.add(item2);


        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("store_uid", this.storeUid);
        rawData.put("order_id", "VR20211001135554");
        rawData.put("items", items);
        rawData.put("echo_0", "");
        rawData.put("echo_1", "");
        rawData.put("echo_2", "");
        rawData.put("echo_3", "");
        rawData.put("echo_4", "");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/voucherrefund");
        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 StoreVoucherRefund() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucherRefund.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        order_id: "VR20211001135554",
        items: [
                   {
                    'id': "H01",
                    'serial_number': "A00052"
                   },
                   {
                    'id': "H01",
                    'serial_number': "A00053"
                   }
               ],
        echo_0: "",
        echo_1: "",
        echo_2: "",
        echo_3: "",
        echo_4: "",

    };
};
/**
 * 取得服務位置
 */
StoreVoucherRefund.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/voucherrefund"
    };
};
/**
 * AES 256 加密
 */
StoreVoucherRefund.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 到主機
 */
StoreVoucherRefund.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucherRefund.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucherRefund.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucherRefund = new StoreVoucherRefund();
StoreVoucherRefund.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 StoreVoucherRefund:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'order_id': "VR20211001135554",
            'items': [
                         {
                          'id': "H01",
                          'serial_number': "A00052"
                         },
                         {
                          'id': "H01",
                          'serial_number': "A00053"
                         }
                     ],
            'echo_0': "",
            'echo_1': "",
            'echo_2': "",
            'echo_3': "",
            'echo_4': "",
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/voucherrefund'
        }

    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)

StoreVoucherRefund = StoreVoucherRefund()
StoreVoucherRefund.run()

回傳 JSON 結構如下:

{
    "code": "230",
    "msg": "退款成功",
    "uid": "129",
    "key": "2d981e1f4a5dda0d3f68eb27b5a23346",
    "order_id": "VR20211001112859",
    "refund_date": "20211001112900",
    "items": [
        {
            "id": "H01",
            "serial_number": "A00052"
        }
    ],
    "echo_0": "",
    "echo_1": "",
    "echo_2": "",
    "echo_3": "",
    "echo_4": ""
}

對未進行使用的票券,發動退款動作。票券購買者需為同一帳號。

特約商店『票券退款交易』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/voucherrefund"}
JSON格式,AES256加密資料
encry_data text 『票券退款交易』欄位參考
JSON格式,AES256加密資料

『票券退款交易』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店退款訂單編號 必填
items array 票券退款資料 必填
每筆『票券退款項目』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

『票券退款交易』回傳欄位

參數名稱 型態 說明 必須
code string 交易回傳碼 『票券退款交易狀態碼』值參考
msg string 回傳訊息
uid string 票券退款交易訂單UID
key string 交易驗証碼
order_id string 特約商店退款訂單編號
refund_date string 退款成功時間(格式(YYYYMMDDHHmmss))
items array 退款票券項目 每筆『票券退款項目』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

『票券退款通知』回報欄位

參數名稱 型態 說明 必須
order_id string 特店商店的退款訂單編號
uid string 票券退款訂單UID
key string 驗証碼
code string 票券退款狀態碼 『票券退款交易狀態碼』值參考
msg string 回傳訊息
refund_date string 退款成功時間(格式(YYYYMMDDHHmmss))
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 StoreVoucherRefundQuery
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     * @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'] = "VR20211001112859";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/voucherrefundquery'
        );
    }
    /**
     * 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;
    }
}

$StoreVoucherRefundQuery = new StoreVoucherRefundQuery();
$StoreVoucherRefundQuery->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 StoreVoucherRefundQuery {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880142";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreVoucherRefundQuery simulator = new StoreVoucherRefundQuery();
            //僅限走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 =  "VR20211001112859";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/voucherrefundquery";
            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 StoreVoucherRefundQuery {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880142";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreVoucherRefundQuery simulator = new StoreVoucherRefundQuery();
        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", "VR20211001112859");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/voucherrefundquery");
        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 StoreVoucherRefundQuery() {
    // 特約商店商務代號
    this.storeUid = "289151880142";
    // 特約商店金鑰或認證碼
    this.storeKey = "g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreVoucherRefundQuery.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        order_id: "VR20211001112859",

    };
};
/**
 * 取得服務位置
 */
StoreVoucherRefundQuery.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/voucherrefundquery"
    };
};
/**
 * AES 256 加密
 */
StoreVoucherRefundQuery.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 到主機
 */
StoreVoucherRefundQuery.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();
    });
};
/**
 * 取得送出欄位資料
 */
StoreVoucherRefundQuery.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreVoucherRefundQuery.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreVoucherRefundQuery = new StoreVoucherRefundQuery();
StoreVoucherRefundQuery.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 StoreVoucherRefundQuery:
    # 特約商店商務代號
    storeUid = "289151880142"
    # 特約商店金鑰或認證碼
    storeKey = b"g4LxEvWTz4ftVrsjei9zIaJLnjXo1QG0"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

    def getRawData(self):
        """取得串接欄位資料

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'order_id': "VR20211001112859",
        }
        return rawData

    def getService(self):
        """取得服務位置

        Returns:
            {dict}: 服務位置資料
        """
        return {
            'service_name': 'api',
            'cmd': 'api/voucherrefundquery'
        }

    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)

StoreVoucherRefundQuery = StoreVoucherRefundQuery()
StoreVoucherRefundQuery.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "content": {
        "order_id": "VR20211001112859",
        "uid": "129",
        "key": "2d981e1f4a5dda0d3f68eb27b5a23346",
        "code": "230",
        "msg": "退款成功",
        "refund_date": "20211001112900",
        "items": [
            {
                "id": "H01",
                "serial_number": "A00052"
            }
        ],
        "echo_0": "",
        "echo_1": "",
        "echo_2": "",
        "echo_3": "",
        "echo_4": ""
    }
}

查詢已發動之票券退款交易,查詢訂單結果。

特約商店『票券退款交易查詢』參數說明

欄位 型態 說明
store_uid string(16) 特約商店商務代號
service text {"service_name": "api", "cmd": "api\/voucherrefund"}
JSON格式,AES256加密資料
encry_data text 『票券退款交易查詢』欄位參考
JSON格式,AES256加密資料

『票券退款交易查詢』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店代碼 必填
order_id string 特約商店訂單編號 必填

『票券退款交易查詢』回傳欄位

參數名稱 型態 說明 必須
code string 票券交易狀態碼
msg string 回傳訊息
content object 查詢內容 『票券退款訂單查詢內容回傳』欄位參考

其他關聯欄位說明

關聯欄位

『消費者資訊』欄位

參數名稱 型態 說明 必須
user_id string 消費者帳號 必填
user_name string 消費者姓名
user_real_name string 消費者真實姓名
user_address_post_zone string 消費者地址郵遞區號
user_address string 消費者帳單地址
user_sn_type string 1:身分證,2:統一證號,3:護照號碼 付款人為本國人為1,外國人2 or 3
user_sn string 付款人身分證/統一證號/護照號碼
user_phone string 消費者家用電話
user_cellphone_code string 消費者行動電話國碼
user_cellphone string 消費者行動電話
user_email string 消費者 E-Mail
user_birthday string 消費者生日
ip string 消費者來源 IP 必填

『票券項目』欄位

參數名稱 型態 說明 必須
id string 票券產品編號(最長限32 Bytes)
限英數字([a-zA-Z0-9])
必填
name string 票券產品名稱 必填
amount integer 購買票券數量 必填
price integer 此批發行票券的每張票券面額 必填
cost integer 此批發行票券的每張票券內含價值 必填
total_price integer 此批票券的面額總和 必填
total_cost integer 此批票券內含價值總和 必填
gift_number integer 票券贈送張數(預設0張) 必填
trust_start_date string 票券履約保證起始日 必填
trust_end_date string 票券履約保證結束日(履約保證時間必須超過一年) 必填
validity_end_date string 票券優惠結束日(格式YYYYmmdd) 必填
is_custom_serial integer 是否使用客製票券序號
0. 沒有 1.有
若客戶類型為「平台客戶」,此欄位必填1.有
必填
serial_numbers string 客製票券序號JSON資料
注意:
1.客製票券序號不能重複,且限英數字([a-zA-Z0-9])
2.長度最長32bytes
3.若客戶類型為「平台客戶」,此欄位必填
4.若購賣票券交易狀態為交易失敗、未進行交易等確定不可再進行交易,客製券號可進行回收再利用
5.如無回收未用客製票券序號機制,則請忽略4的實作,不重複即可
gift_serial_numbers string 客製「贈送票券」序號JSON資料
規則同客製票券序號
sharing object 每張票券分潤比例
總部 + 銷售點 + 核銷點 需等於 1
必填
『票券分潤』欄位參考

『票券宣告』欄位

參數名稱 型態 說明 必須
name string 票券產品名稱 必填
content string 宣告內容,只允許下列html tag,其餘會被移除
h6,ul,li,b

『票券分潤』欄位

參數名稱 型態 說明 必須
headquarters float 總部分潤 必填
sales float 銷售點分潤 必填
reimbursement float 核銷點分潤 必填

『票券核銷項目(request)』欄位

參數名稱 型態 說明 必須
id string 票券產品編號(最長限32 Bytes) 必填
serial_number string 票券號碼 必填

『票券核銷項目(response)』欄位

參數名稱 型態 說明 必須
id string 票券產品編號(最長限32 Bytes)
serial_number string 票券號碼
is_paid integer 價值類型 『票券價值類型』值參考
sharing object 原始分潤比例 『票券分潤』欄位參考
fee object 費用資訊 『票券費用』欄位參考

『票券費用』欄位

參數名稱 型態 說明 必須
cost float 票券價值
gateway_fee integer 金流手續費
escrow_fee integer 信託手續費
issuance_layer integer 發行費收費者 『發行費收費者』值參考
issuance_fee integer 票券票券發行費
headquarters integer 總部分潤
sales integer 銷售點分潤
reimbursement integer 核銷點分潤

『票券退款項目』欄位

參數名稱 型態 說明 必須
id string 票券產品編號(最長限32 Bytes) 必填
serial_number string 票券號碼 必填

『票券退款訂單查詢內容回傳』欄位

參數名稱 型態 說明 必須
order_id string 特店商店的退款訂單編號
uid string 票券退款交易訂單UID
key string 驗証碼
code string 狀態碼 『票券退款交易狀態碼』值參考
msg string 回傳訊息
refund_date string 退款成功時間(格式(YYYYMMDDHHmmss))
items array 票券退款項目 每筆『票券退款項目』欄位參考
echo_0 string 自訂回傳參數 1
echo_1 string 自訂回傳參數 2
echo_2 string 自訂回傳參數 3
echo_3 string 自訂回傳參數 4
echo_4 string 自訂回傳參數 5

值的定義

『票券服務類型』值內容

型態 說明 備註
1 integer 記名票券(預設)

『適用區域類型』值內容

型態 說明 備註
1 integer 單店券(預設)

『是否為預發行』值內容

型態 說明 備註
1 integer 即時發行(預設)

『發行費收費者』值內容

型態 說明 備註
1 integer 經銷商
2 integer 大特店
3 integer 特約商店

『票券價值類型』值內容

型態 說明 備註
1 integer 有償
0 integer 無償

『購買票券交易狀態碼』值內容

型態 說明 備註
100 string 資料不正確
200 string 資料正確
210 string 交易處理中
250 string 交易成功
260 string 交易進行中(超商代碼繳費)
270 string 交易進行中(虛擬帳號)
280 string 交易進行中(WEBATM等導頁式交易)
290 string 交易成功,但金額不符
300 string 交易失敗
380 string 交易逾時
400 string 系統錯誤
500 string 中斷交易(閘道)
600 string 交易成功且結帳 (信用卡)
A0001 string 中斷交易(金流)
A0002 string 未完成交易
B200 string 執行成功
B500 string 執行失敗

『票券核銷交易狀態碼』值內容

型態 說明 備註
100 string 資料不正確
250 string 交易成功
300 string 交易失敗
400 string 系統錯誤。
B200 string 執行成功
B500 string 執行失敗

『票券退款交易狀態碼』值內容

型態 說明 備註
100 string 資料不正確
230 string 退款成功
300 string 退款交易失敗
400 string 系統錯誤。
B200 string 執行成功
B500 string 執行失敗

票券相關費用規則

票券金額計算(票券單張最低50元)

1.特約商店金額 = 票券價值 - 信託手續費(四捨五入,最低1元) - 金流手續費(四捨五入,最低1元) - 票券發行費

總部分潤 + 銷售點分潤 + 核銷點分潤 = 100%

2.核銷點分潤 = (特約商店金額 * 核銷點分潤)四捨五入 3.銷售點分潤 = (特約商店金額 * 銷售點分潤)四捨五入

核銷點分潤 + 銷售點分潤 若超過特約商店金額,負差額由銷售點吸收

4.總部分潤 = 特約商店金額 - 銷核銷點分潤 - 售點分潤

案例1: 票券H000001價值50元、信託手續費2%、手續費1%、票券發行費(由經銷商收)1元 核銷點分潤 50%、銷售點分潤50%

信託手續費 = 50 * 0.02 = 1 元 金流手續費 = 50 * 0.01 = 0.5,故用最低1元計 票券發行費 = 1 元

特約商店金額 = 50 - 1 - 1 - 1= 47 元 核銷點分潤 = 47 * 0.5 = 23.5 元,四捨五入24元 銷售點分潤 = 47 * 0.5 = 23.5 元,四捨五入24元。

47 - 24 - 24 = -1 負差額由銷售點吸收

故銷售點分潤 = 24 + (-1) = 23 元

總部分潤 = 47 - 24 - 23 = 0 元

案例2: 票券H000001價值50元、信託手續費2%、手續費1%、票券發行費(由經銷商收)1元 核銷點分潤 33%、銷售點分潤33%、總部分潤34%

信託手續費 = 50 * 0.02 = 1 元 金流手續費 = 50 * 0.01 = 0.5,故用最低1元計 票券發行費 = 1 元

特約商店金額 = 50 - 1 - 1 -1 = 47 元 核銷點分潤 = 47 * 0.33 = 15.51 元,四捨五入16元。 銷售點分潤 = 47 * 0.33 = 15.51 元,四捨五入16元。

47 - 16 - 16 = 15 正差額

總部分潤 = 47 - 16 - 16 = 15 元

備註:贈送票券部分無費用

附錄一:PFN(支付工具)參數表

資料傳遞可使用編號,也可以使用代碼 。 例如:pfn=27跟pfn=PION,一樣都是使用Pi 拍錢包線上付款支付工具。

編號 代碼 狀態 說明
1 CREDITCARD 啟用 信用卡
3 CSTORECODE 啟用 超商代碼
4 WEBATM 啟用 WEBATM
6 E_COLLECTION 啟用 虛擬帳號 (ATM轉帳)
10 ALIPAY 啟用 支付寶
13 WECHAT 啟用 微信支付
15 LINEPAYON 啟用 LINE線上付款(消費者主掃)
16 LINEPAYOFF 啟用 LINE線下付款(消費者被掃)
19 WECHATOFF 啟用 微信支付線下
20 APPLEPAY 啟用 Apple Pay
21 GOOGLEPAY 啟用 Google Pay
27 PION 啟用 Pi 拍錢包線上付款(消費者主掃)
28 PIOFF 啟用 Pi 拍錢包線下付款(消費者被掃)
29 AMEX 啟用 美國運通
31 JKOON 啟用 街口支付線上付款(消費者主掃)
32 JKOOF 啟用 街口支付線下付款(消費者被掃)
33 ALIPAYOFF 啟用 支付寶線下(消費者被掃)
38 EASYWALLETON 啟用 悠遊付線上付款(消費者主掃)
39 EASYWALLETOFF 啟用 悠遊付線下付款(消費者被掃)

附錄三:設定調整

附錄四:資料加密方式說明

  1. 所有的API送出HTTPs請求之欄位中,service 和 encry_data 欄位皆進行 AES256+BASE64 加密處理。
  2. AES加密,格式為CBC,長度為256bits,金鑰長度32,IV長度16,傳遞內文為加密後組合IV並經過Base64轉換後傳出。
  3. IV資料建議隨機產生。

附錄五:API模擬串接服務

提供模擬使用HTTP Protocol,透過POST方式傳遞資料到MYTIX, 並且得到回傳結果。