NAV
php csharp java javascript python

eDDA電子化授權設計概要

安全性設計

所有的串接要求發動都僅能從特約商店的網頁伺服器發出請求,將傳輸的資料以AES256加密,再透過HTTPS加密傳輸。

資料驗證

串接參數中有一組由雜湊函式運算出的驗証碼,作為資料驗証用,以確保資料正確性。

系統架構

由特約商店的伺服器利用伺服端服務程式發動,以避免資料被竄改或攔截。為確保交易安全,MYPAY僅能接受 https connection。特別注意:系統僅接受使用TLS1.2以上通訊協定。

MYPAY LINK 目前提供之服務與格式

MYPAY LINK 提供eDDA電子化授權應用方式,應用情境如下:

(1)eDDA核印:發動eDDA電子化授權請求,消費者透過網銀或金融卡核印。

(2)eDDA核印查詢:查詢已發動eDDA電子化授權請求之狀態與資訊。

(3)eDDA已核印查詢:查詢指定的帳號和交易代碼,目前eDDA電子化授權允許的資訊清單。

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

eDDA核印

<?php
/**
 * 特約商店串接-eDDA核印
 */
final class StoreAuthorization
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880002";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    /**
     * 串接交易位置
     * @var string
     */
    public $url = "https://pay.usecase.cc/api/init";
    /**
     * 取得串接欄位資料
     * @return array
     */
    public function getRawData()
    {
        $rawData = array();
        $rawData['store_uid'] = $this->storeUid;
        $rawData['user_id'] = "phper";
        $rawData['tix'] = "560";

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

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

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

            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.user_id =  "phper";
            rawData.tix =  "560";

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

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/edda");
        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');

/**
 * 特約商店串接-eDDA核印
 */
function StoreAuthorization() {
    // 特約商店商務代號
    this.storeUid = "289151880002";
    // 特約商店金鑰或認證碼
    this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreAuthorization.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        user_id: "phper",
        tix: "560",

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

StoreAuthorization = new StoreAuthorization();
StoreAuthorization.run();

import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes

"""特約商店串接-eDDA核印
"""
class StoreAuthorization:
    # 特約商店商務代號
    storeUid = "289151880002"
    # 特約商店金鑰或認證碼
    storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

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

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'user_id': "phper",
            'tix': "560",
        }
        return rawData

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

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

    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)

StoreAuthorization = StoreAuthorization()
StoreAuthorization.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "uid": 233,
    "key": "9f20a8c130752776a332586822474c99",
    "url": "https:\/\/pay.usecase.cc\/edda\/BKRg06.html"
}

發動eDDA電子化授權請求,得到網址導頁至核印網址,讓消費者進行透過網銀或金融卡進行核印。 當消費者核印完成後,會背景通知該筆請求之結果至特約商店在管理系統設定之通報網址。

消費者可能核印多筆帳戶。若消費者取消核印時,也會背景通知取消之核印之銀行帳戶。

特約商店『eDDA核印』參數說明

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

『eDDA核印』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店商務代號 必填
user_id string 特約商店帳號 必填
tix integer 核印交易代碼 必填
『eDDA交易代碼』值參考

『eDDA核印』回傳欄位

參數名稱 型態 說明 必須
code string 回傳碼
msg string 回傳訊息
uid string eDDA電子化授權核印訂單流水號
key string 驗証碼
url string 核印網址

『eDDA核印』回報欄位

參數名稱 型態 說明 必須
uid integer eDDA電子化授權核印訂單流水號
key string 驗證碼
code string 核印狀態 『eDDA 核印狀態』值參考
msg string 核印狀態說明
user_id string 消費者帳號
tix string 交易代碼(eDDA授權資訊)
cellphone_code string 手機區碼(eDDA授權資訊)
cellphone string 手機號碼(eDDA授權資訊)
bank_uid string 扣款銀行代碼(eDDA授權資訊)
bank_account string 扣款銀行帳號(eDDA授權資訊)

『eDDA取消核印通知』回報欄位

參數名稱 型態 說明 必須
store_uid string 特約商店商務代號
user_id string 消費者帳號
tix string 交易代碼(eDDA授權資訊)
cellphone_code string 手機區碼(eDDA授權資訊)
cellphone string 手機號碼(eDDA授權資訊)
bank_uid string 扣款銀行代碼(eDDA授權資訊)
bank_account string 扣款銀行帳號(eDDA授權資訊)

eDDA核印查詢

<?php
/**
 * 特約商店串接-eDDA核印查詢
 */
final class StoreQuery
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880002";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    /**
     * 串接交易位置
     * @var string
     */
    public $url = "https://pay.usecase.cc/api/init";
    /**
     * 取得串接欄位資料
     * @return array
     */
    public function getRawData()
    {
        $rawData = array();
        $rawData['store_uid'] = $this->storeUid;
        $rawData['uid'] = "216";
        $rawData['key'] = "b97d40636451eb7e2538f928e6658151";

        return $rawData;
    }
    /**
     * 取得服務位置
     * @return array
     */
    public function getService()
    {
        return array(
            'service_name' => 'api',
            'cmd' => 'api/eddaquery'
        );
    }
    /**
     * AES 256 加密
     * @param array $fields
     * @param string $key
     * @return string
     */
    public function encrypt($fields, $key)
    {
        $data = json_encode($fields);
        $size = openssl_cipher_iv_length('AES-256-CBC');
        $iv   = openssl_random_pseudo_bytes($size);
        $data = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
        $data = base64_encode($iv . $data);
        return $data;
    }
    /**
     * 資料 POST 到主機
     * @param array $postData
     * @return mixed
     */
    public function post($postData = [])
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }
    /**
     * 取得送出欄位資料
     * @return array
     */
    public function getPostData ()
    {
        $postData = array();
        $postData['store_uid'] = $this->storeUid;
        $postData['service'] = $this->encrypt($this->getService(), $this->storeKey);
        $postData['encry_data'] = $this->encrypt($this->getRawData(), $this->storeKey);
        return $postData;
    }
    /**
     * 執行
     */
    public function run()
    {
        $json = $this->post($this->getPostData());
        echo $json;
    }
}

$StoreQuery = new StoreQuery();
$StoreQuery->run();
?>

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Collections;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Dynamic;
/// <summary>
/// 建議使用.net framework4.5以上版本
/// 1.請透過NuGet安裝Newtonsoft.Json,若.net framework小於4.5請安裝Newtonsoft.Json 7.0以下版本
/// 2.若貴司.net framework小於4 可能無法使用dynamic,若是自行組陣列
/// 3.若貴司.net framework小於3.5 可能無法使用AES類別,若是參閱微軟網站使用較舊方式進行加密
/// 4.若貴司.net framework小於3.5 可能沒有Linq可使用,故data只能組字串,Newtonsoft.Json也可能無法使用
/// </summary>
namespace MyPay {
    /// <summary>
    /// 特約商店串接-eDDA核印查詢
    /// </summary>
    public class StoreQuery {
        /// <summary>
        /// 特約商店商務代號
        /// </summary>
        public string storeUid = "289151880002";
        /// <summary>
        /// 特約商店金鑰或認證碼
        /// </summary>
        public string storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
        /// <summary>
        /// 串接交易位置
        /// </summary>
        public string url = "https://pay.usecase.cc/api/init";
        /// <summary>
        /// 執行
        /// </summary>
        static void Main() {
            StoreQuery simulator = new StoreQuery();
            //僅限走https的Tls 1.2以上版本
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
            //發送至遠端
            var result = simulator.Post(simulator.GetPostData());

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

            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.uid =  "216";
            rawData.key =  "b97d40636451eb7e2538f928e6658151";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/eddaquery";
            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;
/**
 * 特約商店串接-eDDA核印查詢
 * 1. jackson-core 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
 * 2. jackson-databind 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
 * 3. jackson-annotations 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
 * 4. Spongy Castle 下載 https://mvnrepository.com/artifact/com.madgag.spongycastle/core
 */
public class StoreQuery {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880002";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreQuery simulator = new StoreQuery();
        String json = simulator.post(simulator.getPostData());
        System.out.print(json);
    }

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

        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("store_uid", this.storeUid);
        rawData.put("uid", "216");
        rawData.put("key", "b97d40636451eb7e2538f928e6658151");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/eddaquery");
        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');

/**
 * 特約商店串接-eDDA核印查詢
 */
function StoreQuery() {
    // 特約商店商務代號
    this.storeUid = "289151880002";
    // 特約商店金鑰或認證碼
    this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreQuery.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        uid: "216",
        key: "b97d40636451eb7e2538f928e6658151",

    };
};
/**
 * 取得服務位置
 */
StoreQuery.prototype.getService = function () {
    return {
        service_name: "api",
        cmd: "api/eddaquery"
    };
};
/**
 * AES 256 加密
 */
StoreQuery.prototype.encrypt = function (fields, key) {
    let eData = JSON.stringify(fields);
    const blockSize = 16;
    const iv = crypto.randomBytes(blockSize);
    const encryptor = crypto.createCipheriv('aes-256-cbc', key, iv);
    let tmpCipher = encryptor.update(Buffer.from(eData));
    let finalCipher = encryptor.final();
    const tempData = Buffer.concat([tmpCipher, finalCipher], tmpCipher.length + finalCipher.length);
    let data = Buffer.concat([iv, tempData], iv.length + tempData.length).toString('base64');
    return data;
};
/**
 * 資料 POST 到主機
 */
StoreQuery.prototype.post = function (postData) {
    return new Promise((res, rej) => {
        let options = {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            rejectUnauthorized: false
        };

        let send_process = httpRequest.request(this.url, options, (api_res) => {
            let res_data = "";
            api_res.on('data', (tmp_data) => {
                res_data += tmp_data;
            });
            api_res.on('end', () => {
                res(res_data);
            });
        });

        send_process.write(JSON.stringify(postData));
        send_process.end();
    });
};
/**
 * 取得送出欄位資料
 */
StoreQuery.prototype.getPostData = function () {
    return {
        "store_uid": this.storeUid,
        "service": this.encrypt(this.getService(), this.storeKey),
        "encry_data": this.encrypt(this.getRawData(), this.storeKey)
    };
};
/**
 * 執行
 */
StoreQuery.prototype.run = async function () {
    json = await this.post(this.getPostData())
    console.log(json);
};

StoreQuery = new StoreQuery();
StoreQuery.run();

import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes

"""特約商店串接-eDDA核印查詢
"""
class StoreQuery:
    # 特約商店商務代號
    storeUid = "289151880002"
    # 特約商店金鑰或認證碼
    storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

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

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'uid': "216",
            'key': "b97d40636451eb7e2538f928e6658151",
        }
        return rawData

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

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

    def encrypt(self, fields, key):
        """AES 256 加密

        Args:
            fields {dict}: 欄位資料
            key {bytes}: AES金鑰

        Returns:
            {string}: 加密資料
        """
        data = json.dumps(fields, separators=(',', ':'))
        data = Padding.pad(data.encode('utf-8'), AES.block_size)
        iv = get_random_bytes(AES.block_size)
        cipher = AES.new(key, AES.MODE_CBC, iv)
        data = cipher.encrypt(data)
        data = base64.b64encode(iv + data)
        return data

    def post(self, postData):
        """資料 POST 到主機

        Args:
            postData {dict}: 欄位資料

        Returns:
            {string}: JSON資料
        """
        result = requests.post(self.url, postData)
        return result.text

    def getPostData(self):
        """取得送出欄位資料

        Returns:
            {dict}: 欄位資料
        """
        postData = {
            'store_uid': self.storeUid,
            'service': self.encrypt(self.getService(), self.storeKey),
            'encry_data': self.encrypt(self.getRawData(), self.storeKey)
        }
        return postData

    def run(self):
        """執行
        """
        json = self.post(self.getPostData())
        print(json)

StoreQuery = StoreQuery()
StoreQuery.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "content": {
        "uid": 216,
        "key": "b97d40636451eb7e2538f928e6658151",
        "code": "C20",
        "msg": "核印成功",
        "user_id": "phper",
        "tix": "560",
        "cellphone_code": 886,
        "cellphone": "924123312",
        "bank_uid": "461",
        "bank_account": "00551234567890"
    }
}

當發動eDDA核印後,可以透過此API發動查詢此筆請求核印結果。

特約商店『eDDA核印查詢』參數說明

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

『eDDA核印查詢』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店商務代號 必填
uid string 訂單號碼 必填
key string 驗証碼 必填

『eDDA核印查詢』回傳欄位

參數名稱 型態 說明 必須
code string 回傳碼
msg string 回傳訊息
content object 『Edda核印訂單查詢』欄位參考

『Edda核印訂單查詢』欄位

參數名稱 型態 說明 必須
uid integer eDDA電子化授權核印訂單流水號
key string 驗證碼
code string 核印狀態 『eDDA 核印狀態』值參考
msg string 核印狀態說明
user_id string 消費者帳號
tix string 交易代碼(eDDA授權資訊)
cellphone_code string 手機區碼(eDDA授權資訊)
cellphone string 手機號碼(eDDA授權資訊)
bank_uid string 扣款銀行代碼(eDDA授權資訊)
bank_account string 扣款銀行帳號(eDDA授權資訊)

eDDA已核印查詢

<?php
/**
 * 特約商店串接-eDDA已核印查詢
 */
final class StoreAuthorizationQuery
{
    /**
     * 特約商店商務代號
     * @var string
     */
    public $storeUid = "289151880002";
    /**
     * 特約商店金鑰或認證碼
     * @var string
     */
    public $storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    /**
     * 串接交易位置
     * @var string
     */
    public $url = "https://pay.usecase.cc/api/init";
    /**
     * 取得串接欄位資料
     * @return array
     */
    public function getRawData()
    {
        $rawData = array();
        $rawData['store_uid'] = $this->storeUid;
        $rawData['user_id'] = "phper";
        $rawData['tix'] = "560";

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

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

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

            dynamic rawData = new ExpandoObject();
            rawData.store_uid = this.storeUid;
            rawData.user_id =  "phper";
            rawData.tix =  "560";

            return rawData;
        }
        /// <summary>
        /// 取得服務位置
        /// </summary>
        private ServiceRequest GetService() {
            ServiceRequest rawData = new ServiceRequest();
            rawData.service_name = "api";
            rawData.cmd = "api/eddaauthorizationquery";
            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;
/**
 * 特約商店串接-eDDA核印查詢
 * 1. jackson-core 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
 * 2. jackson-databind 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind
 * 3. jackson-annotations 下載 https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations
 * 4. Spongy Castle 下載 https://mvnrepository.com/artifact/com.madgag.spongycastle/core
 */
public class StoreQuery {
    /**
     * 特約商店商務代號
     */
    String storeUid = "289151880002";
    /**
     * 特約商店金鑰或認證碼
     */
    String storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    /**
     * 串接交易位置
     */
    String url = "https://pay.usecase.cc/api/init";
    /**
     * 執行
     * @param  args
     */
    public static void main(String[] args) {
        StoreQuery simulator = new StoreQuery();
        String json = simulator.post(simulator.getPostData());
        System.out.print(json);
    }

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

        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("store_uid", this.storeUid);
        rawData.put("uid", "216");
        rawData.put("key", "b97d40636451eb7e2538f928e6658151");

        return rawData;
    }
    /**
     * 取得服務位置
     * @return 串接服務資料
     */
    public Map getService() {
        Map<Object, Object> rawData = new HashMap<Object, Object>();
        rawData.put("service_name", "api");
        rawData.put("cmd", "api/eddaquery");
        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');

/**
 * 特約商店串接-eDDA已核印查詢
 */
function StoreAuthorizationQuery() {
    // 特約商店商務代號
    this.storeUid = "289151880002";
    // 特約商店金鑰或認證碼
    this.storeKey = "KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx";
    // 串接交易位置
    this.url = "https://pay.usecase.cc/api/init";
};
/**
 * 取得串接欄位資料
 */
StoreAuthorizationQuery.prototype.getRawData = function () {
    return {
        store_uid: this.storeUid,
        user_id: "phper",
        tix: "560",

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

StoreAuthorizationQuery = new StoreAuthorizationQuery();
StoreAuthorizationQuery.run();

import json
import base64
import requests
from Crypto.Cipher import AES
from Crypto.Util import Padding
from Crypto.Random import get_random_bytes

"""特約商店串接-eDDA已核印查詢
"""
class StoreAuthorizationQuery:
    # 特約商店商務代號
    storeUid = "289151880002"
    # 特約商店金鑰或認證碼
    storeKey = b"KYTjd9ACcjGaTK6V3zWmMkyrQS08Ndcx"
    # 串接交易位置
    url = "https://pay.usecase.cc/api/init"

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

        Returns:
            {dict}: 欄位資料
        """
        rawData = {
            'store_uid': self.storeUid,
            'user_id': "phper",
            'tix': "560",
        }
        return rawData

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

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

    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)

StoreAuthorizationQuery = StoreAuthorizationQuery()
StoreAuthorizationQuery.run()

回傳 JSON 結構如下:

{
    "code": "B200",
    "msg": "執行成功",
    "rows": [
        {
            "tix": "560",
            "tix_name": "購物(非電子支付機構)",
            "bank_uid": "461",
            "bank_name": "票交所四六一銀行(測試)",
            "bank_account": "00551234567890",
            "cellphone_code": 886,
            "cellphone": "924123312"
        },
        {
            "tix": "560",
            "tix_name": "購物(非電子支付機構)",
            "bank_uid": "461",
            "bank_name": "票交所四六一銀行(測試)",
            "bank_account": "00551234567890",
            "cellphone_code": 886,
            "cellphone": "918123312"
        }
    ]
}

查詢特約商店指定之帳號,已經目前eDDA電子化授權仍有效核印通過清單。

特約商店『eDDA已核印查詢』參數說明

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

『eDDA已核印查詢』欄位

參數名稱 型態 說明 必須
store_uid string 特約商店商務代號 必填
user_id string 特約商店帳號 必填
tix integer 核印交易代碼 必填

『eDDA已核印查詢』回傳欄位

參數名稱 型態 說明 必須
code string 執行結果(B200,B500,100)
msg string 執行結果訊息
rows array 查詢資料清單 每筆『eDDA 核印資訊』欄位參考

『eDDA 核印資訊』欄位

參數名稱 型態 說明 必須
tix integer 交易代碼
tix_name string 交易代碼名稱
bank_uid string 銀行代碼
bank_name string 銀行名稱
bank_account string 銀行帳號
cellphone_code integer 手機國碼
cellphone integer 手機號碼

其他關聯欄位說明

值的定義

『eDDA交易代碼』值內容

型態 說明 備註
530 integer 慈善捐款
560 integer 購物(非電子支付機構)
441 integer 網購退費
902 integer 分期款
903 integer 保全費
904 integer 貨款
908 integer 現金加值
909 integer 租金(營利)
910 integer 會費(營利)
912 integer 仲介服務
405 integer 貸款
803 integer 消費貸款

『eDDA 核印狀態』值內容

型態 說明 備註
C01 string 核印進行中
C02 string 核印進行中(已進入核印頁面)
C03 string 核印進行中(已對票交所發動核印)
C10 string 未進行核印
C20 string 核印成功
C30 string 核印失敗
C40 string 系統異常

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

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

附錄二:API模擬串接服務

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