微信小程序 B2B支付程序C#、.NET

这里只贴出支付的操作,如果 需要其它方法(退款、查询 、关闭订单等)可以发邮件9067874006@qq.com。

需要准备的参数

AppID(小程序ID)wx7405————9e7d

B2B商户17——-637

沙箱AppKeyNYf9s—————–TvgXGCVqf  现网AppKeyHcRiLtJa——————MBoOru

appSecret :获取access_token时使用

session_Key:微信小程序登录时获取的

 

请求对象类

/// <summary>

    /// 创建订单请求对象

    /// </summary>

    public class WxB2bOrderReques

    {

        /// <summary>

        /// 单位分

        /// </summary>

        public decimal Price { get; set; }

        /// <summary>

        /// 订单号

        /// </summary>

        public string Out_trade_no { get; set; }

        /// <summary>

        /// 商品描述

        /// </summary>

        public string Description { get; set; }

    }

    /// <summary>

    /// 退款请求对象

    /// </summary>

    public class RefundRequestModel

    {

        /// <summary>

        /// 商户订单号

        /// </summary>

        public string out_trade_no { get; set; }

        /// <summary>

        /// 退款金额(分)

        /// </summary>

        public decimal refund { get; set; }

        /// <summary>

        /// 退款来源,枚举值 1:人工客服退款   2:用户自己退款   3:其他

        /// </summary>

        public int refund_from { get; set; }

        /// <summary>

        /// 退款订单号

        /// </summary>

        public string out_refund_no { get; set; }

}

响应对象类

 public class BaseResponse

    {

        /// <summary>

        /// 0表示成功

        /// </summary>

        public int errcode { get; set; }

        public string errmsg { get; set; }

    }

    /// <summary>

    /// 退款响应对象

    /// </summary>

    public class RefundResponseModel: BaseResponse

    {

        public string refund_id { get; set; }

        public string out_refund_no { get; set; }

        public string order_id { get; set; }

        public string out_trade_no { get; set; }

        public string data { get; set; }

        public bool IsApplySuccess {

            get

            {

                return errcode == 0;

            }

        }

    }

    /// <summary>

    /// 创建订单响应类

    /// </summary>

    public class CreateOrderRespones

    {

        /// <summary>

        /// 支付签名

        /// </summary>

        public string paySign { get; set; }

        /// <summary>

        /// 用户状态签名

        /// </summary>

        public string signature { get; set; }

        /// <summary>

        /// 签名数据

        /// </summary>

        public string signData { get; set; }

        /// <summary>

        /// 支付类型

        /// 支付类型,不同mode的signData不同,B2b支付固定填retail_pay_goods

        /// 示例值:retail_pay_goods

        /// </summary>

        public string mode { get; set; }

    }

B2b配置类

/// <summary>

    /// 配置类

    /// </summary>

    public class PayB2bConfig

    {

        public string appid { get; set; }

        public string appSecret { get; set; }

        public string mchid { get; set; }

        public string AppKey = “”;

        public string session_Key = “”;

        public string baseUrl = “https://api.weixin.qq.com”;

    }

公共方法

 #region 

 

        public string GetOrderNo(String Tag)

        {

            string strOrderNo = Tag + DateTime.Now.ToString(“yyyyMMddHHmmssfff”) + GenerateNonceStr();

            return strOrderNo;

        }

        public string GenerateNonceStr()

        {

            return GetRandomUInt().ToString();

        }

 

        public uint GetRandomUInt()

        {

            var randomBytes = GenerateRandomBytes(sizeof(uint));

            return BitConverter.ToUInt32(randomBytes, 0);

        }

        private byte[] GenerateRandomBytes(int bytesNumber)

        {

            RNGCryptoServiceProvider csp = new RNGCryptoServiceProvider();

            byte[] buffer = new byte[bytesNumber];

            csp.GetBytes(buffer);

            return buffer;

        }

 /// <summary>

        /// 获取支付签名

        /// </summary>

        /// <param name=”requestCommonPayment”>

        /// uri,切记不可带参数,即去掉”?”及后面的部分

        /// 如果是基础库的wx.requestCommonPayment,uri固定为requestCommonPayment

        /// 其它的 举例:对于https://api.weixin.qq.com/retail/B2b/getorder 来说,uri = /retail/B2b/getorder</param>

        /// <param name=”postdata”></param> 

        /// <returns></returns>

        public string GetPaySign(string uri, string postdata)

        {

            string data = string.Concat(uri, “&”, postdata);

            return HmacSHA256(data, payB2BConfig.AppKey);

        }

        /// <summary>

        /// 获取用户签名

        /// </summary> 

        /// <param name=”postdata”></param>

        /// <param name=”session_key”></param>

        /// <returns></returns>

        public string GetSignature(string postdata)

        {

            return HmacSHA256(postdata, payB2BConfig.session_Key);

        }

  /// 引入  Org.BouncyCastle.Crypto

        /// </summary>

        /// <returns></returns>

        public static string HmacSHA256(string message, string key)

        {  

            byte[] keyBytes = Encoding.UTF8.GetBytes(key); // 使用你的密钥

            byte[] messageBytes = Encoding.UTF8.GetBytes(message); // 要认证的消息

 

            IMac hmac = new HMac(new Sha256Digest()); // 创建 HMAC-SHA256 实例

            hmac.Init(new KeyParameter(keyBytes)); // 初始化 HMAC 实例,设置密钥

 

            hmac.BlockUpdate(messageBytes, 0, messageBytes.Length); // 更新消息

            byte[] result = new byte[hmac.GetMacSize()]; // 获取 HMAC 的大小

            hmac.DoFinal(result, 0); // 计算最终的 HMAC 值

            string hmacHex = BitConverter.ToString(result).Replace(“-“, “”).ToLower(); // 将结果转换为十六进制字符串

            return hmacHex;

        }

/// <summary>

        /// 引入  Org.BouncyCastle.Crypto

        /// </summary>

        /// <returns></returns>

        public static string Sha1Encrypt(string input)

        {

            byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(input);

 

            // 创建SHA1摘要对象

            IDigest digest = new Sha1Digest();

 

            // 更新摘要对象以包含输入数据

            digest.BlockUpdate(inputBytes, 0, inputBytes.Length);

 

            // 计算哈希值

            byte[] hash = new byte[digest.GetDigestSize()];

            digest.DoFinal(hash, 0);

 

            // 将哈希值转换为十六进制字符串

            string hashString = Hex.ToHexString(hash);

            return hashString;

        }

 #endregion

 

1、B2B发起支付

B2B发起支付在服务端不需要请求微信的接口,只要根据参数生成相应的参数返回给微信小程序,然后由微信小程序带着参数进行发起支付。代码如下:

服务端(生成参数)

 /// <summary>

        /// 下单

        /// 小程序接口 wx.requestCommonPayment(Object object)

        /// </summary>

        /// <returns></returns>

        public CreateOrderRespones WxCreatePayOrder(WxB2bOrderReques reques)

        {

            string order_no = GetOrderNo(“P”);

            var postData = new

            {

                mchid = payB2BConfig.mchid,

                out_trade_no = order_no,

                description = reques.Description,

                env = 0,

                amount = new

                {

                    order_amount = Convert.ToInt32(reques.Price * 100) //单位为分

                }

            };

            string jsonEncoding = Newtonsoft.Json.JsonConvert.SerializeObject(postData);

            string paySign = GetPaySign(“requestCommonPayment”, jsonEncoding);

            string signature = GetSignature(jsonEncoding);

//获取参数后返回给微信小程序

            return new CreateOrderRespones()

            {

                mode = “retail_pay_goods”,

                paySign = paySign,

                signature = signature,

                signData = jsonEncoding

            };

   }

  /// <summary>

        /// 创建B2b支付订单

        /// </summary>

        /// <returns></returns>

        public JsonResult RequestToB2bPay()

        { 

            string session_Key = Request[“session_Key”];

            ApiResponeBase<CreateOrderRespones> res = new ApiResponeBase<CreateOrderRespones>();

            res.error = (int)ErrorEnum.Error;

            

            WeChatB2bPayServices payUtils = new WeChatB2bPayServices();

            payUtils.payB2BConfig = GetPayB2bConfig();

            payUtils.payB2BConfig.session_Key = session_Key;

            string orderNo = payUtils.GetOrderNo(“P”);

             

            WxB2bOrderReques wxOrderReques = new WxB2bOrderReques()

            {

                Description = “商品订单支付”,

                Out_trade_no = orderNo ,

                Price = 10 –单位分

            }; 

 

            CreateOrderRespones response = payUtils.WxCreatePayOrder(wxOrderReques);

            res.error = (int)ErrorEnum.Success;

            res.data = response;

            res.msg = “创建支付订单成功!”;

            return Json(res, JsonRequestBehavior.AllowGet);

        }

微信小程序端
const RequestToB2bPay=(session_Key)=>{  

  return new Promise((resove,reject)=>{ 

    httpUitls.httpAjax({

      url:”WChatApi/WeChatB2bPayInfo/RequestToB2bPay”,

      data:{session_Key:session_Key},

      method:”POST”,

      contentType:”FORM”,

      callBack:function(res){  

        let paymentData = res.data

        if (res.error == 0){

          wx.requestCommonPayment({

            mode:paymentData.mode,

            signData:paymentData.signData,

            paySig:paymentData.paySign,

            signature:paymentData.signature,

            success(res) { 

              if(res.errMsg == “requestCommonPayment:ok”){

                resove({isPayed:true,errMsg:”支付成功!”});

              }else{ 

                resove({isPayed:false,errMsg:”支付失败!”+res.errMsg});

              } 

            },

            fail(res) {   

                reject({isPayed:false,errMsg:”支付失败!”+res.errMsg});

            }

          });

        }else{

          reject(“支付失败,请联系管理员”);

        }

      }

    })

  }); 

}  

支付通知

//举例:假设填写的URL=”https://www.qq.com/revice”, Token=”AAAAA”。

 

        //推送的URL链接:https://www.qq.com/revice?signature=f464b24fc39322e44b38aa78f5edd27bd1441696&echostr=4375120948345356249×tamp=1714036504&nonce=1514711492

        //将token、timestamp、nonce三个参数进行字典序排序,排序后结果为:[“1514711492″,”1714036504″,”AAAAA”]。

        //将三个参数字符串拼接成一个字符串:”15147114921714036504AAAAA”

        //进行sha1计算签名:f464b24fc39322e44b38aa78f5edd27bd1441696

        //与URL链接中的signature参数进行对比,相等说明请求来自微信服务器,合法。

        //构造回包返回微信,回包消息体内容为URL链接中的echostr参数4375120948345356249。

        /// <summary>

        /// 以下地址设置到 微信小程序 消息通知中

        /// </summary>

        /// <returns></returns>

        public string ReceiveNotifyByNoEncrypt()

        {

            string Toke = “7302366f3b4d———a70618c50a3”;//与 小程序后台  「开发管理」-「消息推送配置」设置TOKEN一致

            string signature = Request[“signature”];

            string timestamp = Request[“timestamp”];

            string nonce = Request[“nonce”];

            string echostr = Request[“echostr”];

  

            string[] array = new string[] { Toke, timestamp, nonce };

            Array.Sort(array);

            string sign = Cryptography.Sha1Encrypt(string.Join(“”, array));

            if (sign == signature && !string.IsNullOrEmpty(echostr))

            {

                return echostr;

            }

            //一、读取接口请求通知的内容

            string notifyData = WB.BASE.COMMON.HttpServerInteface.GetInputStream();

            WriteNotityLog(notifyData);

            //未读取到请求内容,返回错误响应

            if (string.IsNullOrEmpty(notifyData))

                return “fail”;

            //二、获取请求参数 作为第四名步解密使用

 

            dynamic dynamicObj = JsonConvert.DeserializeObject<dynamic>(notifyData);

            if (dynamicObj.Event == “retail_pay_notify” && dynamicObj.pay_status == “ORDER_PAY_SUCC”)

            {

                string order = dynamicObj.out_trade_no;

                //支付成功

            }

            if (dynamicObj.Event == “retail_refund_notify”)

            {

                string out_refund_no = dynamicObj.out_refund_no;

                string out_trade_no = dynamicObj.out_trade_no;

                //退款成功

            }

            return “success”;

        }

 public static String GetInputStream()

        {

            //接收从微信后台POST过来的数据

            System.IO.Stream s = System.Web.HttpContext.Current.Request.InputStream;

            int count = 0;

            byte[] buffer = new byte[1024];

            StringBuilder builder = new StringBuilder();

            while ((count = s.Read(buffer, 0, 1024)) > 0)

            {

                builder.Append(Encoding.UTF8.GetString(buffer, 0, count));

            }

            s.Flush();

            s.Close();

            s.Dispose();

            return builder.ToString();

        }