using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Text;
using com.unionpay.acp.sdk;
using System.Text.RegularExpressions;
using System.Collections.Specialized;


/**
 * 产品：二维码支付<br>
 * 交易：行业码获取用户授权+获取用户标识+统一下单<br>
 * 日期： 2019-05<br>
 * 版权： 中国银联<br>
**/
namespace upacp_demo_qrc.demo.api_16_qrc
{
    public partial class HymMer : System.Web.UI.Page
    {
        const string ACTION_FILL_ORDER = "fillOrder";
        const string ACTION_PUSH_ORDER = "pushOrder";
        const string ACTION_GET_AUTH = "getUserAuth";
        readonly Encoding ENCODING = System.Text.Encoding.UTF8;

        const string GET_USR_AUTH_URL = "https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=";

        private void Forword(string url, Dictionary<string, string> data)
        {
            HttpContext httpContext = HttpContext.Current;
            foreach (String key in data.Keys)
            {
                String value = data[key];
                httpContext.Items.Add(key, value);
            }
            Server.Transfer(url, false);
        }

        private void Forword(string url)
        {
            Server.Transfer(url, false);
        }

        private void ForwordErr(string errInfo)
        {
            Dictionary<string, string> data = new Dictionary<string, string>();
            data.Add("errInfo", errInfo);
            Forword("/pages/api_16_qrc/hym/Err.aspx", data);
        }

        private Dictionary<string, string> GetAllRequestParam()
        {
            Dictionary<string, string> data = new Dictionary<string, string>();
            NameValueCollection coll = Request.Form;
            foreach (string key in coll.AllKeys)
            {
                data.Add(key, coll[key]);
            }
            coll = Request.QueryString;
            foreach (string key in coll.AllKeys)
            {
                if(!data.ContainsKey(key)) data.Add(key, coll[key]);
            }
            return data;
        }


        protected void Page_Load(object sender, EventArgs e)
        {
            string urlPrefix = Request.Url.ToString();
            int splitIdx = urlPrefix.IndexOf('?');
            if (splitIdx > 0)
                urlPrefix = urlPrefix.Substring(0, splitIdx);
            urlPrefix = urlPrefix + "?action=";

            Response.Charset = "utf-8";
            Response.ContentType = "text/html; charset=utf-8";

            string ua = Request.UserAgent;

            string uaReg = ".*(UnionPay/[^ ]+ ([0-9a-zA-Z]+)).*";
            Match match = Regex.Match(ua, uaReg);

            if (!match.Success)
            {
                ForwordErr("非银联云闪付的user-agent。<br>ua为：<br>" + ua);
                return;
            }
            string appUpIdentifier = match.Groups[1].Value;
            Dictionary<string, string> reqParams = GetAllRequestParam();

            if (!reqParams.ContainsKey("action"))
            {
                //0. 填写订单信息。
                Forword("/pages/api_16_qrc/hym/fillOrderMer.aspx");
                return;
            }

            string action = reqParams["action"];
            reqParams.Remove("action");
            if (ACTION_FILL_ORDER.Equals(action))
            {
                //1. 调获取用户授权。
                string redirectUrl = urlPrefix + ACTION_GET_AUTH + "&" + SDKUtil.CreateLinkString(reqParams, false, true, ENCODING);
                Response.Redirect(GET_USR_AUTH_URL + HttpUtility.UrlEncode(redirectUrl, ENCODING), true);
                return;
            }
            else if (ACTION_GET_AUTH.Equals(action))
            {
                //2. 获取用户授权返回处理，没问题（00、34）的话，调获取用户标识接口，成功的话显示订单信息
                string userAuthCode = reqParams.ContainsKey("userAuthCode") ? reqParams["userAuthCode"] : null;
                string respCode = reqParams.ContainsKey("respCode") ? reqParams["respCode"] : null;
                string merId = reqParams["merId"];

                if (!"00".Equals(respCode) && !"34".Equals(respCode))
                {
                    ForwordErr("获取用户信息失败。respCode=" + respCode);
                    return;
                }

                String appUserId = null;
                Dictionary<string, string> fwdData = new Dictionary<string, string>();
                if (userAuthCode != null)
                {
                    //3. 调获取用户标识接口
                    StringBuilder errMsgBuffer = new StringBuilder();
                    appUserId = GetAppUsrId(merId, userAuthCode, appUpIdentifier, errMsgBuffer);
                    if (appUserId == null)
                    {
                        ForwordErr("调用获取用户标识接口失败，失败信息：" + errMsgBuffer.ToString());
                        return;
                    }
                    fwdData["appUserId"] = appUserId;
                    fwdData["showUserId"] = appUserId;
                }
                else
                {
                    fwdData["showUserId"] = "游客";
                }
                reqParams.Remove("action");
                reqParams["action"] = ACTION_PUSH_ORDER;
                if (appUserId != null) reqParams["appUserId"] = appUserId;
                string paramString = SDKUtil.CreateLinkString(reqParams, false, true, ENCODING);
                fwdData["paramString"] = paramString;
                fwdData["txnAmt"] = reqParams["txnAmt"];
                fwdData["orderId"] = reqParams["orderId"];
                Forword("/pages/api_16_qrc/hym/showOrderMer.aspx", fwdData);
                return;
            }
            else if (ACTION_PUSH_ORDER.Equals(action))
            {
                //4. 触发订单推送接口，返回redirectUrl跳转我们前台网关。
                StringBuilder errMsgBuffer = new StringBuilder();
                String redirectUrl = OrderPush(reqParams, errMsgBuffer);
                if (redirectUrl == null)
                {
                    ForwordErr("调用订单推送接口失败，失败信息：" + errMsgBuffer.ToString());
                    return;
                }
                Response.Redirect(redirectUrl);
                return;
            }
            ForwordErr("参数不正确？");
            return;
        }

        private string GetAppUsrId(string merId, string userAuthCode, string appUpIdentifier, StringBuilder errMsg)
        {

            Dictionary<string, string> param = new Dictionary<string, string>();

            //以下信息非特殊情况不需要改动
            param["version"] = SDKConfig.Version;//版本号
            param["encoding"] = "UTF-8";//编码方式
            param["signMethod"] = SDKConfig.SignMethod;// "01";//签名方法
            param["txnType"] = "00";//交易类型
            param["txnSubType"] = "10";//交易子类
            param["bizType"] = "000000";//产品类型
            param["channelType"] = "08";//渠道类型
            param["accessType"] = "0";//接入类型（0普通商户直接接入）   
            
            //TODO 以下信息需要填写
            param["merId"] = merId;//商户号，请改自己的测试商户号，此处默认取demo演示页面传递的参数
            param["orderId"] = DateTime.Now.ToString("yyyyMMddHHmmssfff");//商户订单号，8-32位数字字母，不能含“-”或“_”，可以自行定制规则
            param["txnTime"] = DateTime.Now.ToString("yyyyMMddHHmmss");//订单发送时间，格式为YYYYMMDDhhmmss，取北京时间
            param["userAuthCode"] = userAuthCode;
            param["appUpIdentifier"] = appUpIdentifier;

            AcpService.Sign(param, System.Text.Encoding.UTF8);  // 签名
            string url = SDKConfig.BackTransUrl;//发送post 请求的地址
            Dictionary<String, String> rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8);

            if (rspData.Count == 0)
            {
                errMsg.Append("post失败。<br>\n");
                return null;
            }
            if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8))
            {
                errMsg.Append("商户端验证返回报文签名失败。<br>\n");
                return null;
            }

            string respCode = rspData["respCode"];
            string respMsg = rspData["respMsg"];
            string appUserId = rspData["appUserId"]; 
            if ("00".Equals(respCode))
            {
                return appUserId;
            }
            errMsg.Append("respCode=" + respCode + ", respMsg=" + respMsg + "。<br>\n");
            return null;
        }

        private string OrderPush(Dictionary<string, string> reqParams, StringBuilder errMsg)
        {

            Dictionary<string, string> param = new Dictionary<string, string>();

            //以下信息非特殊情况不需要改动
            param["version"] = SDKConfig.Version;//版本号
            param["encoding"] = "UTF-8";//编码方式
            param["signMethod"] = SDKConfig.SignMethod;// "01";//签名方法
            param["txnType"] = "01";//交易类型
            param["txnSubType"] = "01";//交易子类
            param["bizType"] = "000000";//产品类型
            param["channelType"] = "08";//渠道类型
            param["accessType"] = "0";//接入类型（0普通商户直接接入）   
            param["currencyCode"] = "156";//交易币种，境内商户固定156
            param["tradeType"] = "mobileWeb";//行业码场景固定送mobileWeb

            //TODO 以下信息需要填写
            param["merId"] = reqParams["merId"];//商户号，请改自己的测试商户号，此处默认取demo演示页面传递的参数
            param["orderId"] = reqParams["orderId"];//商户订单号，8-32位数字字母，不能含“-”或“_”，此处默认取demo演示页面传递的参数，可以自行定制规则
            param["txnTime"] = reqParams["txnTime"];//订单发送时间，格式为YYYYMMDDhhmmss，取北京时间，此处默认取demo演示页面传递的参数，参考取法： DateTime.Now.ToString("yyyyMMddHHmmss")
            param["txnAmt"] = reqParams["txnAmt"];//交易金额，单位分，此处默认取demo演示页面传递的参数
            param["appUserId"] = reqParams["appUserId"];//app用户标识
            param["backUrl"] = SDKConfig.BackUrl;
            if ("true".Equals(reqParams["withFrontUrl"])) param["frontUrl"] = SDKConfig.FrontUrl;

            // 请求方保留域，
            // 透传字段，查询、通知、对账文件中均会原样出现，如有需要请启用并修改自己希望透传的数据。
            // 出现部分特殊字符时可能影响解析，请按下面建议的方式填写：
            // 1. 如果能确定内容不会出现&={}[]"'等符号时，可以直接填写数据，建议的方法如下。
            //param["reqReserved"] = "透传信息1|透传信息2|透传信息3";
            // 2. 内容可能出现&={}[]"'符号时：
            // 1) 如果需要对账文件里能显示，可将字符替换成全角＆＝｛｝【】“‘字符（自己写代码，此处不演示）；
            // 2) 如果对账文件没有显示要求，可做一下base64（如下）。
            //    注意控制数据长度，实际传输的数据长度不能超过1024位。
            //    查询、通知等接口解析时使用System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(reqReserved))解base64后再对数据做后续解析。
            //param["reqReserved"] = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes("任意格式的信息都可以"));

            AcpService.Sign(param, System.Text.Encoding.UTF8);  // 签名

            string url = SDKConfig.OrderTransUrl;//发送post 请求的地址

            Dictionary<String, String> rspData = AcpService.Post(param, url, System.Text.Encoding.UTF8);

            if (rspData.Count == 0)
            {
                errMsg.Append("post失败。<br>\n");
                return null;
            }
            if (!AcpService.Validate(rspData, System.Text.Encoding.UTF8))
            {
                errMsg.Append("商户端验证返回报文签名失败。<br>\n");
                return null;
            }

            string respCode = rspData["respCode"];
            string respMsg = rspData["respMsg"];
            string payUrl = rspData["payUrl"]; 
            if ("00".Equals(respCode))
            {
                payUrl = HttpUtility.UrlDecode(payUrl, ENCODING);
                return payUrl;
            }
            errMsg.Append("respCode=" + respCode + ", respMsg=" + respMsg + "。<br>\n");
            return null;
        }
    }
}