• 使用招商银行云直连服务提现(.Net6)


    以下代码的功能是用户可以实现业务中用户虚拟钱包的钱提现到用户银行卡,其实本质上是把商户的账户资金划转到用户银行卡,其实就是银行转账,相关代码如下:

    复制代码
    ///此方法存在部分业务代码,核心方法是doProcess方法且基本是可以复用的,其它的代码可以根据自身业务场景灵活处理;
    public
    async Task WithDrawToBank(TransferToBankDto transferToBankDto) { decimal balance = await _repository.WalletRepository.GetWalletBalance(new Contract.User.UserBasicDto() { WeChatId = transferToBankDto.WeChatId, }); if (balance < transferToBankDto.Amount) { return new WithDrawResultDto() { WithDrawResult = false, Message = "可用余额不够,请重新输入提现金额。" }; } long userId = await _repository.UserRepository.GetUserIdByWeChatId(transferToBankDto.WeChatId); if (userId == 0) { return new WithDrawResultDto() { WithDrawResult = false, Message = "用户不存在" }; } //bool walletToBankResult = await _walletService.WalletToBankAccount(transferToBankDto.WeChatId, userId, transferToBankDto.Amount, (int)WalletCashType.Withdrawing, yurRef); //if (walletToBankResult) //{ Log.Information("begin WithDraw call cloud direct"); string reqId = DCHelper.GetTime() + RandomHelper.GeneratorRandomStr(7); decimal amount = transferToBankDto.Amount; string bankCode = await _repository.BankRepository.QueryBankCodeByBankAccount(userId, transferToBankDto.BankAccount); string bankHolder = await _repository.BankRepository.QueryCardHolder(userId, transferToBankDto.BankAccount); Log.Information($"bankHolder:{bankHolder}"); //记录业务编号 //业务编号记录到数据库 // 组织发送报文 JObject obj = new JObject(); JObject req = new JObject(); JArray bb1paybmx1BodyArray = new JArray(); JObject bb1paybmx1Body = new JObject(); JObject bb1paybmx1Body2 = new JObject(); JArray bb1payopx1BodyArray = new JArray(); JObject bb1payopx1Body = new JObject(); JObject bb1payopx1Body2 = new JObject(); JObject head = new JObject(); head.Add("funcode", FunCode);// FunCode = "BB1PAYOP";功能代码 head.Add("userid", UID);//用户编号,与商户相关 head.Add("reqid", reqId); bb1paybmx1Body.Add("busCod", BusCod);//BusCod = "N02030";业务编码 bb1paybmx1Body.Add("busMod", BusMod);///业务模式 bb1paybmx1BodyArray.Add(bb1paybmx1Body); //bb1paybmx1Body2.Add("bb1paybmx1", bb1paybmx1BodyArray); JArray bb1payopx1 = new JArray(); bb1payopx1Body.Add("dbtAcc", CompanyCard);//公司账号 bb1payopx1Body.Add("crtAcc", transferToBankDto.BankAccount);//收款人账号 bb1payopx1Body.Add("crtNam", bankHolder);//持卡人姓名 bb1payopx1Body.Add("ccyNbr", CurrencyCode);//币种 CurrencyCode = "10";//10代表人民币 bb1payopx1Body.Add("trsAmt", amount.ToString("#0.00")); //bb1payopx1Body.Add("crtBnk", "招商银行深圳分行"); bb1payopx1Body.Add("bnkFlg", (bankCode == CMBCode ? "Y" : "N"));//CMBCodeCMBCode = "CMB";如果是招商银行,bnkFlg的取值是Y bb1payopx1Body.Add("stlChn", StlChn);//StlChn为R代表及时到账 bb1payopx1Body.Add("nusAge", "钱包提现到银行卡"); //bb1payopx1Body.Add("yurRef", "202008260078000033"); bb1payopx1Body.Add("yurRef", yurRef);//业务流水号,可以使用雪花算法生成 bb1payopx1BodyArray.Add(bb1payopx1Body); JObject jobjectBodyList = new JObject(); jobjectBodyList.Add("bb1paybmx1", bb1paybmx1BodyArray); jobjectBodyList.Add("bb1payopx1", bb1payopx1BodyArray); JObject jobjectBody = new JObject(); jobjectBody.Add("body", jobjectBodyList); jobjectBody.Add("head", head); JObject requestBody = new JObject(); requestBody.Add("request", jobjectBody); //bb1payopx1Body2.Add("bb1payopx1", bb1payopx1BodyArray); // 请求发送接收 var withDrawResult = doProcess(requestBody, FunCode); if (withDrawResult.WithDrawResult == true) { bool walletToBankResult = await _walletService.WalletToBankAccount(transferToBankDto.WeChatId, userId, transferToBankDto.Amount, (int)WalletCashType.Withdrawing, yurRef); WithDrawToBankRecord withDrawToBankRecord = new WithDrawToBankRecord (userId: userId, funcode: FunCode, reqid: reqId, busCod: BusCod, busMod: BusMod, dbtAcc: CompanyCard, crtAcc: transferToBankDto.BankAccount, crtNam: bankHolder, ccyNbr: CurrencyCode, trsAmt: amount, bnkFlg: (bankCode == CMBCode ? "Y" : "N"), stlChn: StlChn, nusAge: "钱包提现到银行卡", yurRef: yurRef.ToString(), withdrawStatus: 0,openId: transferToBankDto.OpenId); await _repository.RedPackageRepository.CreateWithDrawToBankRecord(withDrawToBankRecord); await WithDrawSendMsgToClient.SendMsgToClient(openId: transferToBankDto.OpenId, templageId: SuccessTemplateId, amount: amount, withdrawDatetime: DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), WithdrawToBankStatus.Success); } //更新出金时间 await _repository.BankRepository.UpdateWithDrawDateTimeAsync(userId, transferToBankDto.BankAccount); return withDrawResult; //} //else //{ // return new WithDrawResultDto() { WithDrawResult = false,Message="提现失败,请稍后重试." }; //} }
    复制代码

    DCHelper辅助类

    复制代码
    using Newtonsoft.Json.Linq;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Security;
    using System.Security.Cryptography.X509Certificates;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Demo.Utils
    {
        public class DCHelper
        {
    
            public static string SerialJsonOrdered(JObject json)
            {
                List<string> keyList = new List<string>();
                foreach (var x in json)
                {
                    keyList.Add(x.Key);
                }
                string[] keyArray = keyList.ToArray();
                Array.Sort(keyArray, string.CompareOrdinal);
                StringBuilder appender = new StringBuilder();
                appender.Append("{");
                bool isFirstEle = true;
                foreach (var key in keyArray)
                {
                    if (!isFirstEle)
                    {
                        appender.Append(",");
                    }
                    Object val = json[key];
                    if (val is JObject)
                    {
                        appender.Append("\"").Append(key).Append("\":");
                        appender.Append(SerialJsonOrdered((JObject)val));
                    }
                    else if (val is JArray)
                    {
                        JArray jarray = (JArray)val;
                        appender.Append("\"").Append(key).Append("\":[");
                        bool isFirstArrEle = true;
                        for (int i = 0; i < jarray.Count; i++)
                        {
                            if (!isFirstArrEle)
                            {
                                appender.Append(",");
                            }
                            Object obj = jarray[i];
                            if (obj is JObject)
                            {
                                appender.Append(SerialJsonOrdered((JObject)obj));
                            }
                            else
                            {
                                appender.Append(obj.ToString());
                            }
                            isFirstArrEle = false;
                        }
                        appender.Append("]");
                    }
                    else if (((JToken)val).Type == JTokenType.String)
                    {
                        string value = val.ToString();
                        appender.Append("\"").Append(key).Append("\":").Append("\"").Append(value.Replace("\\", "\\\\")).Append("\"");
                    }
                    else if (((JToken)val).Type == JTokenType.Boolean)
                    {
                        string value = val.ToString().ToLower();
                        appender.Append("\"").Append(key).Append("\":").Append(value);
                    }
                    else
                    {
                        string value = val.ToString();
                        appender.Append("\"").Append(key).Append("\":").Append(value);
                    }
                    isFirstEle = false;
                }
                appender.Append("}");
                return appender.ToString();
            }
    
    
            public static String GetTime()
            {
                return DateTime.Now.ToString("yyyyMMddHHmmssfff");
            }
            public static String GetTime2()
            {
                return DateTime.Now.ToString("yyyyMMddHHmmss");
            }
            // 发送Post请求
            public static String DoPostForm(String httpUrl, Hashtable param)
            {
                Encoding encoding = Encoding.GetEncoding("utf-8");
                HttpWebResponse response = PostHttps(httpUrl, param, encoding);
                Stream stream = response.GetResponseStream();
                StreamReader sr = new StreamReader(stream);
                return sr.ReadToEnd();
            }
    
            private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
            {
                return true; //信任所有https站点证书,不安全,请勿在生产环境中使用。
            }
    
            private static HttpWebResponse PostHttps(string url, Hashtable param, Encoding encoding)
            {
                HttpWebRequest request = null;
                //ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult); // 请勿在生产环境中使用该行程序
                request = WebRequest.Create(url) as HttpWebRequest;
                request.ProtocolVersion = HttpVersion.Version11;
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.Timeout = 15000;
                request.KeepAlive = false;
                request.ReadWriteTimeout = 60000;
    
                byte[] data = encoding.GetBytes(CreateLinkString(param));
                request.ContentLength = data.Length;
                using (Stream stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
    
                WebResponse res;
                try { res = request.GetResponse(); }
                catch (WebException e)
                {
                    res = e.Response;
                }
                return (HttpWebResponse)res;
            }
    
            private static string CreateLinkString(Hashtable param)
            {
                IEnumerator keys = param.Keys.GetEnumerator();
                StringBuilder prestr = new StringBuilder();
                int i = 0;
                while (keys.MoveNext())
                {
                    i++;
                    string key = keys.Current as string;
                    string value = param[key] as string;
                    if (i == param.Count)
                    {
                        prestr.Append(key).Append("=").Append(value);
                    }
                    else
                    {
                        prestr.Append(key).Append("=").Append(value).Append("&");
                    }
                }
                return prestr.ToString();
            }
        }
    }
    复制代码

    RandomHelper辅助类

    复制代码
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Demo.Utils
    {
        public static class RandomHelper
        {
            public static string GeneratorRandomStr(int len)
            {
                string chars = "0123456789abcdefghijklmnopqrstuvwxyz";
    
                Random randrom = new Random((int)DateTime.Now.Ticks);
    
                string str = "";
                for (int i = 0; i < len; i++)
                {
                    str += chars[randrom.Next(chars.Length)];
                }
                return str;
            }
          
        }
    }
    复制代码

     

    核心方法:

    复制代码
     private WithDrawResultDto doProcess(JObject jObject, string FUNCODE)
            {
                JObject obj = new JObject();
                // 签名
                obj.Add("sigdat", "__signature_sigdat__");
                obj.Add("sigtim", DCHelper.GetTime2());
                jObject.Add("signature", obj);
                string source = DCHelper.SerialJsonOrdered(jObject);
                Console.WriteLine("签名原文: " + source);
                Log.Information($"{FUNCODE} request data is {source}");
                Encoding encoding = Encoding.UTF8;
                byte[] signature1 = DCCryptor.CMBSM2SignWithSM3(GetID_IV(), Convert.FromBase64String(privkey), encoding.GetBytes(source));//privkey是私钥
                string sigdat1 = Convert.ToBase64String(signature1);
                Console.WriteLine("签名结果: " + sigdat1);
                obj["sigdat"] = sigdat1;
    
                // SM4-CBC加密
                string plaintxt = jObject.ToString();
                Console.WriteLine("加密前req:  " + plaintxt);
    
                byte[] enInput = DCCryptor.CMBSM4EncryptWithCBC(encoding.GetBytes(sm4key), GetID_IV(), encoding.GetBytes(plaintxt));//sm4key是用户的对称密钥
                string req = Convert.ToBase64String(enInput);
                Console.WriteLine("加密后req:  " + req);
                Log.Information("begin send request to cloud direct");
                // 发送请求
                Hashtable map = new Hashtable();
                map.Add("UID", UID);
                map.Add("ALG", ALG_SM);//ALG_SM = "SM";采用国密算法
                map.Add("DATA", HttpUtility.UrlEncode(req, encoding));
                map.Add("FUNCODE", FUNCODE);
                string res = DCHelper.DoPostForm(URL, map);//访问招商银行的api请求地址:(测试环境:http://cdctest.cmburl.cn/cdcserver/api/v2;正式环境:https://cdc.cmbchina.com/cdcserver/api/v2)
                Log.Information("end request");
                Console.WriteLine("res:  " + res);
                Log.Information("DoPostForm {@res}", res);
                try
                {
                    Convert.FromBase64String(res);
                }
    
                catch (Exception ex)
                {
                    Console.WriteLine("访问返回错误.");
                    Log.Information(" Convert.FromBase64String error  {@ex}", ex);
                    return new()
                    {
                        WithDrawResult = false,
                        Message = "访问返回错误."
                    };
                }
    
                // 解密请求
                string resplain = encoding.GetString(DCCryptor.CMBSM4DecryptWithCBC(encoding.GetBytes(sm4key), GetID_IV(), Convert.FromBase64String(res)));
                Console.WriteLine("res decrypt: " + resplain);
                Log.Information($"{FUNCODE} response data is {resplain}");
                // 验签
                JObject object2 = JObject.Parse(resplain);
                JObject? object3 = object2["signature"] as JObject;
                string? resSign = object3?["sigdat"]?.ToString();
                object3["sigdat"] = "__signature_sigdat__";
                object2["signature"] = object3;
                string resSignSource = DCHelper.SerialJsonOrdered(object2);
                Console.WriteLine("验签原文: " + resSignSource);
                Console.WriteLine("验签签名值: " + resSign);
                bool verify = DCCryptor.CMBSM2VerifyWithSM3(GetID_IV(), Convert.FromBase64String(bankpubkey), encoding.GetBytes(resSignSource), Convert.FromBase64String(resSign));//bankpubkey:银行公钥
                Console.WriteLine("验签结果: " + verify);
                Log.Information($"验签结果:{verify}");
                if (verify)
                {
                    //验签成功以后正常判断
                    if (object2["response"]?["head"]?["resultcode"] is not null)
                    {
                        string? resultCode = object2["response"]?["head"]?["resultcode"]?.ToString();
                        if (resultCode != "SUC0000")
                        {
                            Log.Information($"withdraw hava error,resultmsg is {object2["response"]?["head"]?["resultmsg"]?.ToString()}," +
                                   $"resultcode is {object2["response"]?["head"]?["resultcode"]?.ToString()}");
                            return new()
                            {
                                WithDrawResult = false,
                                Message = object2["response"]?["head"]?["resultmsg"]?.ToString()
                            };
                        }
                        else
                        {
                            string[] errors = new[] { "F", "B", "R", "D", "C" };
                            string? rtnFlg = object2["response"]?["body"]?["bb1payopz1"][0]?["rtnFlg"]?.ToString();
                            if (object2["response"]?["body"]?["bb1payopz1"][0]?["reqSts"]?.ToString() == "FIN" && errors.Contains(rtnFlg))
                            {
                                Log.Information($"withdraw hava error,errTxt is {object2["response"]?["body"]?["bb1payopz1"][0]?["errTxt"]?.ToString()}," +
                                    $"errCod is {object2["response"]?["body"]?["bb1payopz1"][0]?["errCod"]?.ToString()}");
                                return new()
                                {
                                    WithDrawResult = false,
                                    Message = object2["response"]?["body"]?["bb1payopz1"][0]?["errTxt"]?.ToString()
                                };
                            }
                        }
                    }
                }
                else
                {
                    Log.Error("验签失败");
                    return new()
                    {
                        WithDrawResult = false,
                        Message = "验签失败,请联系系统管理员。"
                    };
                }
                return new() { WithDrawResult = true };
            }
    复制代码

    私有方法

    复制代码
     private byte[] GetID_IV()
            {
                String uid = UID; // 请替换为实际的用户UID
                String userid = uid + "0000000000000000";
                return Encoding.UTF8.GetBytes(userid.Substring(0, 16));
            }
    复制代码

     

    DCCryptor辅助类

    复制代码
    using Org.BouncyCastle.Asn1;
    using Org.BouncyCastle.Asn1.X9;
    using Org.BouncyCastle.Crypto;
    using Org.BouncyCastle.Crypto.Parameters;
    using Org.BouncyCastle.Crypto.Signers;
    using Org.BouncyCastle.Math;
    using Org.BouncyCastle.Math.EC;
    using Org.BouncyCastle.Security;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Demo.Utils
    {
        public class DCCryptor
        {
            public static byte[] CMBSM4EncryptWithCBC(byte[] key, byte[] iv, byte[] input)
            {
                if (key == null || iv == null || input == null)
                {
                    throw new Exception("CMBSM4EncryptWithCBC 非法输入");
                }
                return CMBSM4Crypt(key, iv, input, true);
            }
    
            public static byte[] CMBSM4DecryptWithCBC(byte[] key, byte[] iv, byte[] input)
            {
                if (key == null || iv == null || input == null)
                {
                    throw new Exception("CMBSM4DecryptWithCBC 非法输入");
                }
                return CMBSM4Crypt(key, iv, input, false);
            }
    
            public static byte[] CMBSM2SignWithSM3(byte[] id, byte[] privkey, byte[] msg)
            {
                if (privkey == null || msg == null)
                {
                    throw new Exception("CMBSM2SignWithSM3 input error");
                }
                ECPrivateKeyParameters privateKey = EncodePrivateKey(privkey);
                SM2Signer signer = new SM2Signer();
                ParametersWithID parameters = new ParametersWithID(privateKey, id);
                signer.Init(true, parameters);
                signer.BlockUpdate(msg, 0, msg.Length);
                return DecodeDERSignature(signer.GenerateSignature());
            }
    
            public static bool CMBSM2VerifyWithSM3(byte[] id, byte[] pubkey, byte[] msg, byte[] signature)
            {
    
                if (pubkey == null || msg == null || signature == null)
                {
                    throw new Exception("CMBSM2VerifyWithSM3 input error");
                }
                ECPublicKeyParameters publicKey = EncodePublicKey(pubkey);
                SM2Signer signer = new SM2Signer();
                ParametersWithID parameters = new ParametersWithID(publicKey, id);
                signer.Init(false, parameters);
                signer.BlockUpdate(msg, 0, msg.Length);
                return signer.VerifySignature(EncodeDERSignature(signature));
            }
    
            private static byte[] CMBSM4Crypt(byte[] keyBytes, byte[] iv, byte[] input, bool forEncrypt)
            {
                KeyParameter key = ParameterUtilities.CreateKeyParameter("SM4", keyBytes);
                ParametersWithIV ivParameterSpec = new ParametersWithIV(key, iv);
                IBufferedCipher cipher = CipherUtilities.GetCipher("SM4/CBC/PKCS7Padding");
                cipher.Init(forEncrypt, ivParameterSpec);
                return cipher.DoFinal(input);
            }
    
            private static ECPrivateKeyParameters EncodePrivateKey(byte[] value)
            {
                BigInteger d = new BigInteger(1, value);
                X9ECParameters spec = ECNamedCurveTable.GetByName("sm2p256v1");
                ECDomainParameters ecParameters = new ECDomainParameters(spec.Curve, spec.G, spec.N, spec.H, spec.GetSeed());
                return new ECPrivateKeyParameters(d, ecParameters);
            }
    
            public static ECPublicKeyParameters EncodePublicKey(byte[] value)
            {
                byte[] x = new byte[32];
                byte[] y = new byte[32];
                Array.Copy(value, 1, x, 0, 32);
                Array.Copy(value, 33, y, 0, 32);
                BigInteger X = new BigInteger(1, x);
                BigInteger Y = new BigInteger(1, y);
                X9ECParameters spec = ECNamedCurveTable.GetByName("sm2p256v1");
                ECPoint Q = spec.Curve.CreatePoint(X, Y);
                ECDomainParameters ecParameters = new ECDomainParameters(spec.Curve, spec.G, spec.N, spec.H, spec.GetSeed());
                return new ECPublicKeyParameters(Q, ecParameters);
            }
    
            private static byte[] DecodeDERSignature(byte[] signature)
            {
                Asn1InputStream stream = new Asn1InputStream(signature);
                Asn1Sequence primitive = (Asn1Sequence)stream.ReadObject();
                System.Collections.IEnumerator enumeration = primitive.GetEnumerator();
                enumeration.MoveNext();
                BigInteger R = ((DerInteger)enumeration.Current).Value;
                enumeration.MoveNext();
                BigInteger S = ((DerInteger)enumeration.Current).Value;
                byte[] bytes = new byte[64];
                byte[] r = Format(R.ToByteArray());
                byte[] s = Format(S.ToByteArray());
                Array.Copy(r, 0, bytes, 0, 32);
                Array.Copy(s, 0, bytes, 32, 32);
                return bytes;
            }
    
            private static byte[] EncodeDERSignature(byte[] signature)
            {
                byte[] r = new byte[32];
                byte[] s = new byte[32];
                Array.Copy(signature, 0, r, 0, 32);
                Array.Copy(signature, 32, s, 0, 32);
                Asn1EncodableVector vector = new Asn1EncodableVector();
                vector.Add(new DerInteger(new BigInteger(1, r)));
                vector.Add(new DerInteger(new BigInteger(1, s)));
                return (new DerSequence(vector)).GetEncoded();
            }
    
            private static byte[] Format(byte[] value)
            {
                if (value.Length == 32)
                {
                    return value;
                }
                else
                {
                    byte[] bytes = new byte[32];
                    if (value.Length > 32)
                    {
                        Array.Copy(value, value.Length - 32, bytes, 0, 32);
                    }
                    else
                    {
                        Array.Copy(value, 0, bytes, 32 - value.Length, value.Length);
                    }
                    return bytes;
                }
            }
        }
    }
    复制代码

     

  • 相关阅读:
    北京大学肖臻老师《区块链技术与应用》公开课笔记:比特币原理(三):分叉、匿名性
    数说方法论:针对个体消费者做批量定制化营销
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    前后端交互常见的几种数据传输格式 form表单+get请求 form表单+post请求 json键值对格式
    深入理解Numpy中sum求和的axis参数
    设计模式之美——里式替换原则 和 接口隔离原则
    图文并茂——队列的实现 ,C语言实现
    Android Logcat 命令行工具
    day14 - 提取图像信息
    回溯算法 | 子集问题(递增子序列) | leecode刷题笔记
  • 原文地址:https://www.cnblogs.com/cby-love/p/17179462.html