您的位置:首页 > 游戏 > 游戏 > 线上学编程哪个机构比较好_seo搜索引擎的优化_艾滋病多久可以查出来_网盟推广平台

线上学编程哪个机构比较好_seo搜索引擎的优化_艾滋病多久可以查出来_网盟推广平台

2025/2/14 0:24:32 来源:https://blog.csdn.net/leigelg/article/details/142821280  浏览:    关键词:线上学编程哪个机构比较好_seo搜索引擎的优化_艾滋病多久可以查出来_网盟推广平台
线上学编程哪个机构比较好_seo搜索引擎的优化_艾滋病多久可以查出来_网盟推广平台

需求要求:用户点开链接直接进行付款,原来打算使用微信H5进行支付,但是审核太麻烦,审核和多次都没通过,并且H5支付只能通过浏览器打开链接,然后唤醒微信进行支付,如果在微信中点开链接是无法支付的。然后采用微信jsapi方案进行支付。支付前需要申请公众号,支付商户号,商户号和公众号进行绑定。支付整体逻辑,用户点开链接,静默获取用户openId(生成支付订单的时候会用到),生成预支付订单,获取到预支付订单的参数后,前端换起微信支付。

准备阶段,我就直接使用另外一名博主的截图,自己难得取图了,具体代码和这位博主有些区别,因为我是直接使用微信官方的sdk。在实际开发中也遇到一些坑,后面会描述。微信公众号-JSAPI支付(保姆级教程)_微信jsapi支付-CSDN博客

微信公众号-JSAPI支付

1、微信公众号配置

https://mp.weixin.qq.com/cgi-bin/home
① 开通网页授权域名(前置条件:需开通网页授权接口权限)
在这里插入图片描述

②暴露网络授权域名
在这里插入图片描述
③获取基本配置中的AppID、AppSecret
在这里插入图片描述

④微信支付关联商户号
在这里插入图片描述

2、微信支付配置

https://pay.weixin.qq.com/
① 配置API证书 获取证书序列号
在这里插入图片描述
②获取正式序列号
在这里插入图片描述
在这里插入图片描述

③设置APIV3秘钥
在这里插入图片描述
④ 产品中心—开通JSAPI支付

在这里插入图片描述
⑤添加支付域名配置
在这里插入图片描述
⑥暴露域名
在这里插入图片描述
⑦AppId账号设置-商户关联公众号
在这里插入图片描述

程序配置:

①配置yml文件

wx:# 公众号appidappId:xxxxxd5381a6# 公众号 appSecretappSecret: ccccccaff013ebe0eff1# 商户号mchId: 123456# 商户APIV3密钥apiV3Key: ccccccccJCcMw466spcSAyV# 商户证书序列号merchantSerialNumber: cccccccF237C29CC# 微信回调地址v3PayNotifyUrl: https://xxxx.cn/orderPay/payNotify

② 微信支付平台下载的秘钥证书放入项目resources目录下

pom文件需要引入微信sdk

        <!--微信支付--><dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-java</artifactId><version>0.2.12</version></dependency>

在生产预支付订单的解密密钥时候我遇到一个很奇怪的报错

经过各方资料查询需要引入这个版本的包确实解决了
 

        <dependency><groupId>org.jetbrains.kotlin</groupId><artifactId>kotlin-stdlib</artifactId><version>1.3.50</version></dependency>

主要流程及代码实现
①前端通过url获取code
(url参数配置)
Appid:公众号的appid
Redirect_uri:重定向至微信支付页面,携带用户openId
Scope:snsapi_base(授权方式 无需弹窗静默授权)
https://open.weixin.qq.com/connect/oauth2/authorize?appid=xx&redirect_uri=xxx&response_type=code&scope=snsapi_base&state=STATE&connect_redirect=1#wechat_redirect

②通过前端获取的code,传入指定支付接口(Redirect_uri),获取openId
1)通过appID、appSecret、code构建请求URL,解析返回结果获取openId

可参考微信官方文档:网页授权 | 微信开放文档
————————————————

       

    /** 前端域名地址*/@Value("${ccsMobileUrl}")private String ccsMobileUrl;/*** 通过code获取用户openId,并重定向到支付页面* @param request* @return* @throws Exception*/@GetMapping("/tpPayOrderPage")public ResponseEntity<String> tpPayOrderPage(HttpServletRequest request) throws Exception {String cardBusinessNo = request.getParameter("cardBusinessNo");String code = request.getParameter("code");String openId = getOpenId(code);String redirectUrl = ccsMobileUrl+"#/payOrder?cardBusinessNo="+cardBusinessNo+"&openId="+openId;return ResponseEntity.status(HttpStatus.FOUND).header("Location", redirectUrl).build();}

重定向到前端的地址为:https://ccc.cn/mobile/#/payOrder?cardBusinessNo=CBABCX2407220002&openId=12345t

跳转到前端页面后,准备进行支付

整个前端代码 先贴出来

<template><div class="root-layout"><div class="title-1">尊敬的客户:您的订单已生成,请确认订单信息</div><div class="title-line"></div><div class="item-title">商品信息</div><div class="item-line" v-if="form.lineList && form.lineList.length>0" :key="key"  v-for="(item, key) in form.lineList"><van-image class="item_img"   :src="item.picUrl" /><div class="item-content"><span class="item-name">{{item.itemName}}</span><span>数量 {{item.quantity}}张</span><span>单价 ¥{{item.unitPrice}}</span></div></div><div v-else><van-empty description="没有行信息" /></div><div class="title-line"></div><div class="title-2"><div class="price">合计:2839元</div><template>  <button class="submit" :class="{ disabled: isPaid }" @click="onClickVersion" :disabled="isPaid">  {{ isPaid ? '已支付' : '确认付款' }}  </button>  
</template></div></div></template><script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import { CellGroup, Field, Image as VanImage } from 'vant'
import { Toast } from 'vant'declare global {interface Window {WeixinJSBridge: any;}
}
@Component({name: 'AboutUs',components: {[Field.name]: Field,[CellGroup.name]: CellGroup,[VanImage.name]: VanImage,},
})
export default class PayOrder extends Vue {form: any = {};isPaid = true; // 用于跟踪支付状态  mounted() {document.title = '订单支付';this.queryCardBusinessInfoHandler();}private onClickVersion() {const weixinJSBridge = window.WeixinJSBridge;if(typeof weixinJSBridge == "undefined") {Toast('请使用微信浏览器打开页面')}else{// 调用接口生成预支付订单this.producePrepayOrder();}}//查询订单详情queryCardBusinessInfoHandler() {// {//   //cardBusinessNo: this.$route.query.cardBusinessNo,//   cardBusinessNo: 'CBABCX2407220002',// }Http.queryCardBusinessInfo<any, any>(this.$route.query.cardBusinessNo).then((res) => {if(res.code=== 500) {this.$toast(res.chnDesc);return}this.form = res || {};if(res.paymentStatus == "1"){this.isPaid = false}else{this.isPaid = true}console.log('this.form', this.form)});}//查询订单详情producePrepayOrder() {const weixinJSBridge = window.WeixinJSBridge;Http.producePrepayOrder({cardBusinessNo: this.$route.query.cardBusinessNo,openId: this.$route.query.openId}).then((res: any) => {if(res.code=== 500) {this.$toast(res.chnDesc);return}console.log('this.res', res)weixinJSBridge.invoke('getBrandWCPayRequest', {"appId": res.appId, // 公众号名称,由商户传入     "timeStamp": res.timeStamp, // 时间戳,自1970年以来的秒数     "nonceStr": res.nonceStr, // 随机串     "package": res.packageVal,"signType": res.signType, // 微信签名方式     "paySign": res.paySign // 微信签名 },(res: { err_msg: string }) => { // 指定 res 的类型// 支付成功if (res.err_msg === "get_brand_wcpay_request:ok") {// 支付成功逻辑Toast('支付成功')}// 支付过程中用户取消else if (res.err_msg === "get_brand_wcpay_request:cancel") {// 取消支付逻辑Toast('取消支付')}// 支付失败else if (res.err_msg === "get_brand_wcpay_request:fail") {// 失败逻辑Toast('支付失败')}// 其它错误处理else if (res.err_msg === "调用支付JSAPI缺少参数:total_fee") {// 错误处理逻辑Toast('支付错误')}location.reload();});});}
}
</script><style lang="less" scoped>
.item-line{margin-top: 5px;padding: 0,10px;display: flex;justify-content: space-between;.item_img{width: 120px;}.item-content{margin-left: 10px;padding: 5px;display: flex;justify-content: space-evenly;flex-direction: column;line-height: 30px;flex: 1;.item-name{white-space: nowrap;      /* 确保文本在一行内显示 */overflow: hidden;         /* 隐藏超出容器的部分文本 */text-overflow: ellipsis;  /* 超出部分显示为省略号 */width: 200px;}}}
.root-layout{padding: 20px 10px
}
.title-1{border: 2px solid #CECECE;width: 99%;height: 100px;text-align: center;line-height: 6;
}
.title-line{border: 2px solid #CECECE;height: 22px;background: #CECECE;
}
.item-title{margin-top: 6%;margin-left: 6%;
}
.title-2 {height: 60px;display: flex; /* 启用Flexbox布局 */justify-content: space-between; /* 子元素分布在容器的两端 */align-items: center; /* 子元素在交叉轴上的对齐方式,这里为居中 */margin-top: 12%;
}.price {/* 移除高度和行高设置,因为Flexbox会自动处理这些 */margin-left: 5%; /* 如果需要的话,可以保留 *//* 如果你想让价格文本垂直居中,可以不需要设置line-height,因为align-items已经处理了 */
}.submit {text-align: right; /* 文本靠右对齐 */padding: 0 2.66667vw; /* 使用视口宽度单位设置内边距 */background-color: #E96C2D; /* 背景色 */border: 1px solid #ccc; /* 边框色,但通常你会想要与背景色形成对比的边框色 */cursor: pointer; /* 鼠标悬停时显示指针样式 */height: 100%; /* 占据父容器的全部高度 */width: 23%; /* 占据父容器的23%宽度 */color: white; /* 文本颜色为白色 */display: flex; /* 启用Flexbox布局 */align-items: center; /* 使子项(文本)在垂直方向上居中 */justify-content: flex-end; /* 如果你还想要文本在水平方向上靠右(尽管text-align: right;已经做了这件事,但在这里是冗余的) */border-radius: 10px;}
.submit.disabled {  background-color: #ccc;  cursor: not-allowed;  
} </style>

1 先queryCardBusinessInfoHandler()查询订单详情,展示订单基本信息

2 点击确认付款按钮进入onClickVersion()准备支付,jsapi只能在微信浏览器中进行支付,所以这里判断了是否是在微信浏览器中操作。

3 方法producePrepayOrder() 生成预支付订单,调用了后端orderPay/prepay 的接口,后端方法后面会贴出来

4 生成预支付订单后,前端weixinJSBridge.invoke换起 微信支付。这里前端的操作基本上就到此结束。

前端换起微信支付可参考微信官方文档:开发指引 - JSAPI支付 | 微信支付服务商文档中心

后端准备开始生成预支付订单

controller

    @PostMapping("/prepay")public BaseResponse<OrderWechatPayResponseDTO> wehchatPay(@RequestBody WechatPayReqDTO request) {return orderWechatPayFacade.wehchatPay(request);}

/** 商户号*/@Value("${wx.mchId}")private String mchId;/** 公众号appid*/@Value("${wx.appId}")private String appId;/** 公众号appSecret*/@Value("${wx.appSecret}")private String appSecret;/** 商户APIV3密钥*/@Value("${wx.apiV3Key}")private String apiV3Key;/** 商户证书序列号 */@Value("${wx.merchantSerialNumber}")private String merchantSerialNumber;/**微信回调地址*/@Value("${wx.v3PayNotifyUrl}")private String v3PayNotifyUrl;
/*** 微信支付生成预支付订单* @param reqDTO* @return*/public OrderWechatPayResponseDTO wehchatPay(WechatPayReqDTO reqDTO) {OrderWechatPayResponseDTO result = new OrderWechatPayResponseDTO();OrderCardBusinessHeadResponseDTO orderWechatPayRespDTO = new OrderCardBusinessHeadResponseDTO();log.info("wehchatPay-reqDTO===={}",JSON.toJSONString(reqDTO));// 支付前校验参数checkParms(reqDTO,orderWechatPayRespDTO);// 获取apiclient_key.pem文件File keyFile = getKeyFile();// 订单号//元转换为分// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错if (config == null) {config =new RSAAutoCertificateConfig.Builder().merchantId(mchId).privateKeyFromPath(keyFile.getPath()).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();}// 构建serviceif (service == null) {service = new JsapiServiceExtension.Builder().config(config).build();}//组装预约支付的实体// request.setXxx(val)设置所需参数,具体参数可见Request定义PrepayRequest request = new PrepayRequest();//计算金额Amount amount = new Amount();// 金额单位为分 需要乘以100amount.setTotal(orderWechatPayRespDTO.getOrderTotalPrice().multiply(new BigDecimal("100")).intValue());amount.setCurrency("CNY");request.setAmount(amount);//公众号appIdrequest.setAppid(appId);//商户号request.setMchid(mchId);//支付者信息Payer payer = new Payer();payer.setOpenid(reqDTO.getOpenId());request.setPayer(payer);//描述request.setDescription("支付测试");//微信回调地址,需要是https://开头的,必须外网可以正常访问//本地测试可以使用内网穿透工具,网上很多的request.setNotifyUrl(v3PayNotifyUrl);//订单号request.setOutTradeNo(orderWechatPayRespDTO.getCardBusinessNo());PrepayWithRequestPaymentResponse payment = null;String errorMsg = "";// 加密try {payment = service.prepayWithRequestPayment(request);log.info("支付成功:{}", JSON.toJSONString(payment));}catch (Exception e){log.error("支付失败:{}",e.getMessage());errorMsg = e.getMessage();}finally {// 支付前将记录保存到支付日志表中saveWechatPayLog(payment,request,errorMsg);}//默认加密类型为RSApayment.setSignType("MD5");//返回数据,前端调起支付BeanUtils.copyProperties(payment,result);return result;}

使用官方sdk方法构建config的时候 这里在获取apiclient_key.pem文件 一直遇到一个问题,本地调试可以读取到密钥内容,但是打包后发版到服务器就获取不到了,通过资料查询,打包后的密钥文件打包到class文件下使用FileInputStream读取不到文件。

所以这里单独写了一个读取密钥文件的方法,如果没有读取到就创建文件并保存到服务器指定目录

/*** 读取密钥文件* @return*/public File getKeyFile() {File keyFile = new File(TEMP_DIR, KEY_FILE_NAME);// 检查文件是否已存在if (keyFile.exists()) {return keyFile;}// 如果不存在,创建并保存文件ClassPathResource keyClassPath = new ClassPathResource("key/apiclient_key.pem");try (InputStream inputStream = keyClassPath.getInputStream();FileOutputStream outputStream = new FileOutputStream(keyFile)) {byte[] buffer = new byte[1024];int length;while ((length = inputStream.read(buffer)) > 0) {outputStream.write(buffer, 0, length);}// 返回新生成的文件return keyFile;} catch (IOException e) {throw new RuntimeException("Unable to load key file", e);}}

在我的代码中,有一些对订单是否能够完成支付的校验,这个方法看自己是否需要checkParms(reqDTO,orderWechatPayRespDTO);

生产预支付订单方法wehchatPay(),我这里返回的实体类是OrderWechatPayResponseDTO,你可以直接返回微信的实体类 PrepayWithRequestPaymentResponse 是一样的

前端获取到预支付订单,换起微信支付,支付成功后,微信会调用我们回调接口,通知支付成功,这里把后端回调的方法贴出来

controller

@ApiOperation(value = "支付回调")@PostMapping("/payNotify")public synchronized void payNotify(HttpServletRequest request){WechatPayNotifyReqDTO reqDTO = new WechatPayNotifyReqDTO();log.info("------收到支付通知controller------");try (ServletInputStream inputStream = request.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"))) {// 读取请求体StringBuilder sb = new StringBuilder();String line;while ((line = bufferedReader.readLine()) != null) {sb.append(line);}String notify = sb.toString();log.info("支付通知内容:" + notify);// 请求头处理...reqDTO.setSignature(request.getHeader("Wechatpay-Signature"));reqDTO.setNonce(request.getHeader("Wechatpay-Nonce"));reqDTO.setTimestamp(request.getHeader("Wechatpay-Timestamp"));reqDTO.setSerial(request.getHeader("Wechatpay-Serial"));reqDTO.setSignType(request.getHeader("Wechatpay-Signature-Type"));reqDTO.setNotify(notify);log.info("reqDTO====={}", JSON.toJSONString(reqDTO));orderWechatPayFacade.payNotify(reqDTO);} catch (Exception e) {log.error("解析付款通知出错:{}", e.getMessage(), e);}}

service

/*** 支付回调* @param request*/public void payNotify(WechatPayNotifyReqDTO request) {log.info("------收到支付通知------");File keyFile = getKeyFile();// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错if (config == null) {config =new RSAAutoCertificateConfig.Builder().merchantId(mchId).privateKeyFromPath(keyFile.getPath()).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();}// 请求头Wechatpay-SignatureString signature = request.getSignature();// 请求头Wechatpay-nonceString nonce = request.getNonce();// 请求头Wechatpay-TimestampString timestamp = request.getTimestamp();// 微信支付证书序列号String serial = request.getSerial();// 签名方式String signType = request.getSignType();// 构造 RequestParamcom.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder().serialNumber(serial).nonce(nonce).signature(signature).timestamp(timestamp).signType(signType).body(request.getNotify()).build();// 初始化 NotificationParserNotificationParser parser = new NotificationParser(config);// 以支付通知回调为例,验签、解密并转换成 Transactionlog.info("验签参数:{}", requestParam);Transaction transaction = parser.parse(requestParam, Transaction.class);log.info("验签成功!-支付回调结果:{}", transaction.toString());Map<String, String> returnMap = new HashMap<>(2);returnMap.put("code", "FAIL");returnMap.put("message", "失败");if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", }}