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;//输出图片的URLvar 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_TenpayNotifyTransactionsRequestData 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": "商户证书序列号有误。"
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/781153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Matlab与数学计算

原文地址&#xff1a;Matlab与数学计算 - Pleasure的博客 下面是正文内容&#xff1a; 前言 这是一篇笔记。主要用于介绍MatLab的作用以及其作为数学工具的使用方法。 目的是总结学校课件复习自用&#xff0c;但是不可能像相关的书籍那么系统全面&#xff0c;力求简单明了。都…

pygame用自带函数绘制三角形 计算重心坐标

三角形重心坐标公式 三角形重心的坐标可以通过其三个顶点的坐标计算得出&#xff0c;公式为((X1X2X3)/3,(Y1Y2Y3)/3)。12 这是因为在平面直角坐标系中&#xff0c;重心的坐标是顶点坐标的算术平均数 中间黑点是重心坐标 import pygame from pygame.locals import * import sy…

scanf/fscanf/sscanf和printf/fprintf/sprintf的使用和对比

一&#xff1a;函数的对比 scanf&#xff1a;从标准输入流中读取格式化数据&#xff08;通常是键盘&#xff09; printf&#xff1a;将格式化数据输出到标准输出流&#xff08;通常是屏幕&#xff09; fscanf&#xff1a;适用于所有输入流的格式化输入函数&#xff08;一般从…

网络安全入门 5 天速成教程_ WEB 安全渗透攻防技术

前言 随着 Web 技术发展越来越成熟&#xff0c;而非 Web 服务越来越少的暴露在互联网上&#xff0c;现在互联网安全主要指的是 Web 安全。 为了自身不“裸奔”在大数据里&#xff0c;渐渐开始学习 Web 安全&#xff0c;在学习 Web 安全的过程中&#xff0c;发现很大一部分知识…

Codeforces Round 838 (Div. 2) D. GCD Queries

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

实验室开放项目实验报告-01

实验室开放项目实验报告 实验名称&#xff1a;实验一输入输出格式 实验目的&#xff1a;熟练掌握程序设计竞赛中通常采用的输入输出格式和掌握不同格式输入输出数据的处理方法 实验内容&#xff1a; 在本地电脑中新建一个文件夹&#xff0c;用于存放C源程序&#xff0c;文件…

【进程OI】基本文件操作的系统调用

文章目录 前言open参数flags参数mode writereadclose 前言 当用户想要向磁盘中的文件读写数据&#xff0c;就必须要得到操作系统的允许。同样&#xff0c;操作系统为了能让用户去对文件进行打开、读写、关闭等操作&#xff0c;向上提供了相应的系统调用的接口。C、JAVA、C等语…

27. UE5 RPG同步面板属性(三)

在前两篇中&#xff0c;我们在C中实现了对GameplayTag的创建&#xff0c;并且创建DataAsset存储数据&#xff0c;按照之前的规划&#xff1a; 首先我们需要通过c去实现创建GameplayTag&#xff0c;这样可以在c和UE里同时获取到Tag创建一个DataAsset类&#xff0c;用于设置tag对…

2024/3/29打卡 填充——贪心

目录 题目 思路 代码 先来说下什么时候使用贪心和动态规划&#xff1a; 一个题目当寻找答案的过程中有大约 2的指数级&#xff08; 2^n&#xff09;个方案的时候&#xff0c;可以考虑用贪心和动态规划问题&#xff08;其实&#xff0c;我现在还不知道什么时候用 dfs 和 动态…

深度学习pytorch——数据增强(持续更新)

背景介绍 大量的数据是防止过拟合的关键&#xff0c;但是我们如何去获取大量的数据&#xff0c;是自己去拍摄、录制吗&#xff1f;显然这种方式有极高的成本。我们可以对同一张图片进行变换得到多张图片&#xff0c;比如原来只有10张图片&#xff0c;通过变换变成了20张图片&a…

2023年第十四届蓝桥杯大赛软件类省赛C/C++研究生组真题(代码完整题解)

C题-翻转⭐ 标签:贪心 简述:如果 S 中存在子串 101 或者 010,就可以将其分别变为 111 和 000,操作可以无限重复。最少翻转多少次可以把 S 变成和 T 一样。 链接: 翻转 思路:要求步骤最少->S每个位置最多修改一次->从头开始遍历不匹配就翻转->翻转不了就-1 …

低代码平台与自动化软件开发的关系

引言 随着信息技术的不断发展&#xff0c;软件开发领域也在不断演进。在追求更高效、更快速的软件开发过程中&#xff0c;低代码平台和自动化软件开发技术日益受到关注。低代码平台以其可视化开发界面和快速构建应用的能力&#xff0c;为非专业开发人员提供了参与软件开发的机会…

Typora for Mac/Win:让Markdown编辑更高效,创作更自由

在数字化时代&#xff0c;文本编辑已成为我们日常生活与工作中的重要环节。Markdown作为一种轻量级标记语言&#xff0c;以其简洁、易读、易写的特性&#xff0c;受到了广大用户的喜爱。而Typora&#xff0c;作为一款专为Markdown设计的文本编辑器&#xff0c;更是让Markdown编…

Midjourney绘图欣赏系列(十四)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

香港服务器怎么看是CN2 GT线路还是CN2 GIA线路?

不知道有没有小伙伴们注意过&#xff0c;很多人在租用香港服务器的时候都习惯性选择 CN2 线路&#xff1f;仿佛香港服务器是否采用 CN2 线路成为个人企业选择香港服务器的一个标准。其实&#xff0c;香港服务器有CN2、优化直连(163)、BGP多线(包含了国际和国内线路)&#xff0c…

(C语言)fgets与fputs函数详解

目录 1. fputs函数详解 1.1 向文件流输入数据 1.2 向标准输出流输出数据 2. fgets函数详解 2. 1 从文件流中得到数据 2.2 从标准输入流读取数据 1. fputs函数详解 头文件&#xff1a;stdio.h 函数有两个参数&#xff1a;str 与 stream 作用&#xff1a;写一串字符串到流…

零拷贝技术、常见实现方案、Kafka中的零拷贝技术的使用、Kafka为什么这么快

目录 1. 普通拷贝 2. 数据拷贝基础过程 2.1 仅CPU方式 2.2 CPU&DMA方式 3.普通模式数据交互 4. 零拷贝技术 4.1 出现原因 4.2 解决思路 4.2.1 mmap方式 4.2.2 sendfile方式 4.2.3 sendfileDMA收集 4.2.4 splice方式 5. Kafka中使用到的零拷贝技术 参考链接 本…

【学习笔记】java项目—苍穹外卖day03

文章目录 苍穹外卖-day03课程内容1. 公共字段自动填充1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3 步骤三 1.4 功能测试1.5 代码提交 2. 新增菜品2.1 需求分析与设计2.1.1 产品原型2.1.2 接口设计2.1.3 表设计 2.2 代码开发2.2.1 文件上传实现2.2.2 新…

uniapp 微信小程序 输入框跟随手机键盘弹起

需求&#xff1a;手机键盘弹起后&#xff0c;页面底部的输入框跟随弹起&#xff0c;且页面不被顶上去 html: <textareaclass"textinput"placeholder-class"input-place"auto-height:maxlength"2000"v-model"text"placeholder"…

微机原理-基于8086倒计时多路抢答器系统

**单片机设计介绍&#xff0c;微机原理-基于8086倒计时多路抢答器系统 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 微机原理-基于8086倒计时多路抢答器系统概要主要关注于利用8086微处理器设计和实现一个具有倒计时功能的多路抢答器系统…