4.C#对接微信Native支付(调用支付下单生成二维码接口)

在完成了前边几篇文章的操作后,我们接下来需要写实际的业务接口。调用微信的native下单接口。

手先看下官网的api文档,https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml

大概的流程是:商户后台系统先调用微信支付的Native下单接口,微信后台系统返回链接参数code_url,商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。
code_url有效期为2小时,过期后扫码不能再发起支付。

在这里插入图片描述

详细业务流程说明:
(1)商户后台系统根据用户选购的商品生成订单。
(2)用户确认支付后调用微信支付【Native下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url。
(4)商户后台系统根据返回的code_url生成二维码。
(5)用户打开微信“扫一扫”扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户在微信客户端输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。

话不多说,上代码。

生成订单调用Native下单API

using Microsoft.AspNetCore.Mvc;
using Senparc.Weixin.TenPayV3.Apis.BasePay;
using Senparc.Weixin.TenPayV3.Apis;
using Senparc.Weixin.TenPayV3.Entities;
using Senparc.Weixin.TenPayV3;
using Senparc.CO2NET.HttpUtility;
using Senparc.Weixin.Entities;
using Senparc.Weixin.Helpers;
using System.Collections.Concurrent;
using Senparc.Weixin;
using Senparc.Weixin.MP.AdvancedAPIs.MerChant;
using Identification.Domain.Shared.Helper;
using Senparc.Weixin.Exceptions;
using Senparc.Weixin.TenPayV3.Apis.BasePay.Entities;
using Senparc.Weixin.TenPayV3.Apis.Entities;
using Senparc.CO2NET.Extensions;
using Microsoft.AspNetCore.Authorization;
using Identification.Domain.Shared.CustomAttribute;
using Identification.Domain.Shared.ExtensionMethod;
using Serilog;
using Newtonsoft.Json;

[Route("tenpay")]
[ApiController]
[Authorize]
public class WeChatPaymentController : ControllerBase
{
    private static TenPayV3Info _tenPayV3Info;

    public static TenPayV3Info TenPayV3Info
    {
        get
        {
            if (_tenPayV3Info == null)
            {
                var key = TenPayHelper.GetRegisterKey(Config.SenparcWeixinSetting);

                _tenPayV3Info =
                    TenPayV3InfoCollection.Data[key];
            }
            return _tenPayV3Info;
        }
    }

    /// <summary>
    /// 用于初始化BasePayApis
    /// </summary>
    private readonly ISenparcWeixinSettingForTenpayV3 _tenpayV3Setting;

    private readonly BasePayApis _basePayApis;
    private readonly SenparcHttpClient _httpClient;

    /// <summary>
    /// trade_no 和 transaction_id 对照表
    /// TODO:可以放入缓存,设置有效时间
    /// </summary>
    public static ConcurrentDictionary<string, string> TradeNumberToTransactionId = new ConcurrentDictionary<string, string>();

    private readonly IServiceProvider _service;
    private readonly ICurrentUserService _currentUser;
    public WeChatPaymentController(SenparcHttpClient httpClient, IServiceProvider service, ICurrentUserService currentUser)
    {
        _tenpayV3Setting = Senparc.Weixin.Config.SenparcWeixinSetting.TenpayV3Setting;
        _basePayApis = new BasePayApis(_tenpayV3Setting);
        this._httpClient = httpClient;
        _service = service;
        _currentUser = currentUser;
    }

    /// <summary>
    /// 使用 Native 支付
    /// </summary>
    /// <param name="input">入参自己定义 我只定义了金额</param>
    /// <returns></returns>
    [HttpPost("nativepay")]
    [SkipMyActionFilter]
    public async Task<ActionResult> NativePayCodeAsync(PaymentOrderParameterDto input)
    {
		//在这里大家可以根据自己的业务逻辑,在入参定义产品的编号,然后验证产品存不存在
		//do some thing


        //使用 Native 支付,输出二维码并展示
        MemoryStream fileStream = null;//输出图片的URL
        var price = (int)(input.amount * 100);//单位为分  这里表示10元=1000分
        var name = $"产品名称{input.amount}元";
        var sp_billno = string.Format("{0}{1}{2}", TenPayV3Info.MchId/*10位*/, SystemTime.Now.ToString("yyyyMMddHHmmss"),
                        TenPayV3Util.BuildRandomStr(6));
        Log.Information($"生成商户唯一单号:{sp_billno}");
        var notifyUrl = TenPayV3Info.TenPayV3Notify;//支付回调地址,必须要在appsettings.json中配置属性为:TenPayV3_TenpayNotify

        TransactionsRequestData requestData = new(TenPayV3Info.AppId, TenPayV3Info.MchId, name, sp_billno, new TenpayDateTime(DateTime.Now.AddHours(1)), null, notifyUrl, null, new() { currency = "CNY", total = price }, null, null, null, null);

        BasePayApis basePayApis = new BasePayApis();
        var result = await basePayApis.NativeAsync(requestData);
        Log.Information($"调用下单api返回结果:{JsonConvert.SerializeObject(result.ResultCode)}");

        //进行安全签名验证
        if (result.VerifySignSuccess == true)
        {
            fileStream = QrCodeHelper.GerQrCodeStream(result.code_url);
            // 将MemoryStream的内容转换为字节数组  
            byte[] byteArrayInMemoryStream = fileStream.ToArray();
            // 将字节数组转换为Base64字符串  
            string base64String = Convert.ToBase64String(byteArrayInMemoryStream);
            //在这个步骤可以把订单记录保存,等到微信回调通知根据单号更新支付状态
            //var paymentService = _service.GetRequiredService<IPaymentOrderService>();
            //await paymentService.AddPaymentOrderAsync(new PaymentOrderAdd { total = input.amount, createOrderTime = DateTime.Now, orderNumber = sp_billno, qrCode = base64String, organizationId = Convert.ToInt32(_currentUser.organizationId), organizationName = _currentUser.organizationName });
            //这里我返回了base64的图片,当然也可以直接返回图片,根据自己的实际需求来选择
            //return File(fileStream, "image/png"); 接返回图片了
            return Ok(new { qrCode = base64String, out_trade_no = sp_billno });
        }
        else
        {
            fileStream = QrCodeHelper.GetTextImageStream("Native Pay 未能通过签名验证,无法显示二维码");
            // 将MemoryStream的内容转换为字节数组  
            byte[] byteArrayInMemoryStream = fileStream.ToArray();
            // 将字节数组转换为Base64字符串  
            string base64String = Convert.ToBase64String(byteArrayInMemoryStream);
            return Ok(new { qrCode = base64String, out_trade_no = "" });
        }
        //return File(fileStream, "image/png"); 不直接返回图片了
    }
}

生成二维码的帮助类

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading.Tasks;
using ZXing;
using ZXing.Common;

namespace Test.Domain.Shared.Helper
{
    public static class QrCodeHelper
    {
        /// <summary>
        /// 生成二维码
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static MemoryStream GerQrCodeStream(string url)
        {
            BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, 300, 300);
            var bw = new ZXing.BarcodeWriterPixelData();

            var pixelData = bw.Write(bitMatrix);
            var bitmap = new System.Drawing.Bitmap(pixelData.Width, pixelData.Height, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            var fileStream = new MemoryStream();
            var bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, pixelData.Width, pixelData.Height), System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);
            try
            {
                // we assume that the row stride of the bitmap is aligned to 4 byte multiplied by the width of the image   
                System.Runtime.InteropServices.Marshal.Copy(pixelData.Pixels, 0, bitmapData.Scan0, pixelData.Pixels.Length);
            }
            finally
            {
                bitmap.UnlockBits(bitmapData);
            }

            fileStream.Flush();//.net core 必须要加
            fileStream.Position = 0;//.net core 必须要加

            bitmap.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);

            fileStream.Seek(0, SeekOrigin.Begin);
            return fileStream;
        }

        /// <summary>
        /// 获取文字图片信息
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static MemoryStream GetTextImageStream(string text)
        {
            MemoryStream fileStream = new MemoryStream();
            var fontSize = 14;
            var wordLength = 0;
            for (int i = 0; i < text.Length; i++)
            {
                byte[] bytes = Encoding.Default.GetBytes(text.Substring(i, 1));
                wordLength += bytes.Length > 1 ? 2 : 1;
            }
            using (var bitmap = new System.Drawing.Bitmap(wordLength * fontSize + 20, 14 + 40, System.Drawing.Imaging.PixelFormat.Format32bppRgb))
            {
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.ResetTransform();//重置图像
                    g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                    g.DrawString(text, new Font("宋体", fontSize, FontStyle.Bold), Brushes.White, 10, 10);
                    bitmap.Save(fileStream, System.Drawing.Imaging.ImageFormat.Png);
                }
            }
            fileStream.Seek(0, SeekOrigin.Begin);
            return fileStream;
        }
    }
}

测试

调用接口可以看到下图返回支付二维码的base64图片,我们只需要将其转换成图片扫码支付即可。
在这里插入图片描述
将base64转换成图片
在这里插入图片描述
在这里插入图片描述

特别注意:
本地环境测试只要是https环境都可以调通微信的下单api,但是到线上所有请求都必须是https请求(包括回调地址)。
如果托管到iis,需要在应用程序池—高级设置—进程模型—加载用户配置文件—选择True,如果不进行此步骤设置会在请求微信下单api时返回以下错误

{
	"code": "SIGN_ERROR",
	"detail": {
		"detail": {
			"issue": "sign not match"
		},
		"field": "signature",
		"location": "authorization",
		"sign_information": {
			"method": "POST",
			"sign_message_length": 392,
			"truncated_sign_message": "POST\n/v3/pay/transactions/native\n17112312146\naochng2o.icb\n{\"appid\"\n",
			"url": "/v3/pay/transactions/native"
		}
	},
	"message": "商户证书序列号有误。"
}

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
优化支付宝、微信、QQ钱包对接SDK,可以通过以下几个方面进行改进: 1. 提高接口稳定性:针对支付宝、微信、QQ钱包的SDK,可以对接口进行优化,提高接口的稳定性和可靠性。通过持续的测试和监控,及时解决可能出现的接口异常问题,确保支付功能的正常运行。 2. 简化对接流程:对接SDK的流程进行简化,减少对接过程中的繁琐步骤,提高对接的效率。可以提供清晰的文档和示例代码,方便开发人员进行对接和调试,降低对接的技术门槛。 3. 提供灵活的支付方式选择:在对接SDK时,可以考虑提供灵活的支付方式选择,满足不同商户和用户的需求。例如,支持扫码支付、刷脸支付微信小程序支付等不同的支付方式,增加支付的便利性和选择性。 4. 加强支付安全性:支付宝、微信、QQ钱包作为大型的支付平台,安全性是非常重要的。在对接SDK时,可以加强支付的安全性,包括数据加密传输、接口访问权限控制等措施,确保用户支付信息的保密性和支付过程的安全性。 5. 提供多样化的支付功能:在对接SDK时,可以考虑提供多样化的支付功能,满足不同商户的需求。例如,支持优惠券、积分支付等功能,提升支付的灵活性和吸引力,增加用户的支付意愿。 通过上述优化措施,可以提升支付宝、微信、QQ钱包对接SDK的性能和用户体验,为商户和用户提供更好的支付服务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香煎三文鱼

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值