使用招商銀行雲直連服務提現(.Net6)

来源:https://www.cnblogs.com/cby-love/archive/2023/03/04/17179462.html
-Advertisement-
Play Games

以下代碼的功能是用戶可以實現業務中用戶虛擬錢包的錢提現到用戶銀行卡,其實本質上是把商戶的賬戶資金劃轉到用戶銀行卡,其實就是銀行轉賬,相關代碼如下: ///此方法存在部分業務代碼,核心方法是doProcess方法且基本是可以復用的,其它的代碼可以根據自身業務場景靈活處理;public async Ta ...


以下代碼的功能是用戶可以實現業務中用戶虛擬錢包的錢提現到用戶銀行卡,其實本質上是把商戶的賬戶資金劃轉到用戶銀行卡,其實就是銀行轉賬,相關代碼如下:

///此方法存在部分業務代碼,核心方法是doProcess方法且基本是可以復用的,其它的代碼可以根據自身業務場景靈活處理;
public
async Task<WithDrawResultDto> 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 非法輸入");
            }
            	   

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 詳細代碼如下: package main import ( "fmt" "strings" ) func main() { s := "1w1w2d2d3d3d4w4w3w3w7d7d8d8d" mj := InitMahjong(s) mj.Print() fmt.Println(mj.Win() ...
  • 背景 記賬強迫症患者,苦於賬本上的信用卡額度總跟實際的對不上,python小白的我決定寫個小demo輔助對賬。 涉及 python BeautifulSoup SQLite 準備 信用卡賬單eml(這裡用的J行) 錢跡賬單csv 關鍵步驟 解析並處理信用卡賬單 使用BeautifulSoup組件,解 ...
  • 題目傳送門 題目描述 我們可以用這樣的方式來表示一個十進位數: 將每個阿拉伯數字乘以一個以該數字所處位置為指數,以 1010 為底數的冪之和的形式。例如 123123 可表示為 1 \times 10^2+2\times 10^1+3\times 10^01×102+2×101+3×100 這樣的形 ...
  • 原創:扣釘日記(微信公眾號ID:codelogs),歡迎分享,非公眾號轉載保留此聲明。 簡介 現如今,有兩種常見的軟體資源幾乎成了Java後端程式的標配,即線程池與連接池,但這些池化資源非常的重要,一旦不夠用了,就會導致程式阻塞、性能低下,所以有時我們需要看看它們的使用情況,以判斷這裡是否是瓶頸。 ...
  • Spring擴展 1.自定義攔截器 spring mvc攔截器根spring攔截器相比,它裡面能夠獲取HttpServletRequest和HttpServletResponse等web對象實例。 spring mvc攔截器的頂層介面是:HandlerInterceptor,包含三個方法: preH ...
  • 這篇文章主要討論分散式系統中的網路分區問題,網路分區是指在分散式集群中,節點之間由於網路不通,導致集群中的節點形成不同的子集,子集中的節點可以相互通信,子集之間的網路是不通的。 ...
  • 簡介 chatgpt-java是一個OpenAI的Java版SDK,支持開箱即用。目前以支持官網全部Api。支持最新版本GPT-3.5-Turbo模型以及whisper-1模型。增加chat聊天對話以及語音文件轉文字,語音翻譯。 開源地址:https://github.com/Grt1228/cha ...
  • 1、簡介 EFcore,可用使得開發人員不需要再去關註資料庫的實現,全都由代碼進行生成 這樣有利於減少工作量、資料庫快速遷移... 2、上手搭建架構 (這個圖是做完本章內容的完整圖,我們一步步深入即可) 在寫EF之前,先安裝好資料庫,我選擇在本地安裝Sqlserver 我們先執行最核心的兩步,將EF ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...