前提:对小程序开发有一定的基础;小程序已发布使用,已开通微信支付,关联商户号。
微信小程序平台:小程序平台
微信开发者文档:开发者文档
微信小程序支付API 地址:微信支付文档地址
微信支付平台:微信支付平台地址
开通微信支付和商户号,关联商户号【需公司信息、银行账户等,按提示逐步完成即可】:

登录微信支付商户平台- 账户中心 - 账户设置 -API安全-申请证书、设置秘钥【按提示逐步完成即可,秘钥必须32位】,使用超级管理员账号进行设置;下载API证书apiclient_cert.p12的文件

Java后端使用的是jeecgboot框架
1)pom.xml 引入依赖
<!--微信小程序-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>4.2.0</version>
</dependency>
<!--微信小程序 支付-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.2.0</version>
</dependency>
2)application.yml配置
#小程序
wechat:
appid:
secret:
mchId: #微信支付商户号
mchKey: #微信支付商户密钥
keyPath: classpath:apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
tradeType: JSAPI #JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
notifyUrl: https://www.XXXX.com/wx/wxOrder/notify: #微信支付异步回掉地址,通知url必须为直接可访问的url,不能携带参数.
keyPath对应的apiclient_cert.p12文件就是在微信商户平台下载的API证书文件
3)微信小程序配置文件
(1)微信小程序配置文件:
@AllArgsConstructor
@Configuration
@ConditionalOnClass({WxMaService.class, WxPayService.class})
@EnableConfigurationProperties(WxProperties.class)
public class WxConfig {
private final WxProperties wxProperties;
/**
* 小程序配置
* @return
*/
@Bean
@ConditionalOnMissingBean
public WxMaConfig wxMaConfig() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid(this.wxProperties.getAppId());
config.setSecret(this.wxProperties.getSecret());
return config;
}
@Bean
public WxMaService wxMaService(WxMaConfig maConfig) {
WxMaService service = new WxMaServiceImpl();
service.setWxMaConfig(maConfig);
return service;
}
/**
* 支付配置
*
* @return
*/
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.wxProperties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.wxProperties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.wxProperties.getMchKey()));
payConfig.setKeyPath(StringUtils.trimToNull(this.wxProperties.getKeyPath()));
payConfig.setTradeType(StringUtils.trimToNull(this.wxProperties.getTradeType()));
payConfig.setNotifyUrl(StringUtils.trimToNull(this.wxProperties.getNotifyUrl()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
(2)微信小程序属性
@Data
@Component
@ConfigurationProperties(prefix = "wechat")
public class WxProperties {
private String appId;
private String secret;
//微信支付商户号
private String mchId;
//微信支付商户密钥
private String mchKey;
// p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath;开头)
private String keyPath;
private String tradeType;
private String notifyUrl;
}
参数、结果接收按照自己的代码实际情况设置:
// 登录动作
doLogin: function () {
let that = this;
wx.login({
success: function (loginRes) {
if (loginRes.code) {
wx.getUserInfo({
withCredentials: true, // 非必填, 默认为true
success: function (infoRes) {
//获取openid
wx.request({
url: loginUrl,
data: {
code: loginRes.code, // 临时登录凭证
rawData: infoRes.rawData, // 用户非敏感信息
signature: infoRes.signature, // 签名
encryptedData: infoRes.encryptedData, // 用户敏感信息
ivStr: infoRes.iv // 解密算法的向量
},
success: function (res) {
res = res.data;
if (res.code == 1) {
that.globalData.userInfo = JSON.parse(infoRes.rawData);
wx.setStorageSync('openid', res.data.openId);
wx.setStorageSync('loginFlag', res.data.sessionKey);
} else {
that.showInfo(res.info);
}
},
fail: function (error) {
//调用接口失败
}
});
},
fail: function (error) {
// 获取 userInfo 失败,去检查是否未开启权限
}
});
} else {
// 获取 code 失败
}
},
fail: function (error) {
// 调用 wx.login 接口失败
}
});
},
访问后端的url: loginUrl对应的登录获取openid方法:
controller层:
/**
* 执行微信端登录
*
* @param code : 微信登录code
* @param encryptedData : 包括敏感数据在内的完整用户信息的加密数据
* @param ivStr : 加密算法的初始向量
* @return : org.dpkj.bean.Result
*/
@RequestMapping("login")
public Result login(String code, String encryptedData, String ivStr) {
Result result = new Result();
try {
WxUserBean result1 = userService.doLogin(code, encryptedData, ivStr);
result.setCode(ResultState.OK);
result.setInfo("oponId获取成功");
result.setData(result1);
} catch (Exception ex) {
ex.printStackTrace();
logger.info(ex.getMessage());
result.setInfo("接口异常,请联系管理员!");
result.setCode(ResultState.NO);
}
return result;
}
serviceImpl层:
@Autowired
private WxMaService wxMaService;
@Override
public WxUserBean doLogin(String code, String encryptedData, String ivStr) throws Exception {
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(sessionInfo.getSessionKey(), encryptedData, ivStr);
//数据库操作
bean.setSessionKey(sessionInfo.getSessionKey());
return bean;
}
微信支付API接口:支付API接口
参数、结果接收按照自己的代码实际情况设置:
startTestPay: function (e) {
let data = {
pickPhone: wx.getStorageSync("phoneNumber"),
openId: wx.getStorageSync("openid"),
testid: id,
title: title,
}
if (data.pickTest && data.pickPhone && data.openId) {
//正则判断手机号
if (/^[1][3,4,5,7,8,9][0-9]{9}$/.test(data.pickPhone)) {
data.amount = price * 100; //把价格换成分
if (app.globalData.userInfo) {
//插入订单
if(price > 0){
wx.request({
url: _createOrder,
data: data,
method: 'post',
success: function (res) {
if (res.data.success) {
let resData = res.data.result;
//吊起微信支付
wx.requestPayment({
nonceStr: resData.nonceStr,
package: resData.packageValue,
paySign: resData.paySign,
timeStamp: resData.timeStamp,
signType: resData.signType,
success() {
console.log("支付成功");
wx.navigateTo({
url: url
})
},
fail() {
//微信支付出现问题,请稍后再试
}
})
} else {
}
},
fail: function (res) {
//检查用户出现问题,请稍后再试
},
});
}else{
//价格为0,直接进入目标页面
wx.navigateTo({
url: url
})
}
} else {
wx.showToast({
title: '微信环境异常,请重新进入小程序后尝试',
icon: 'none'
})
}
} else {
wx.showToast({
title: '请填写正确的手机号码',
icon: 'none'
})
}
} else {
wx.showToast({
title: '请完善用户信息',
icon: 'none'
})
}
},
controller层
/**
* @param data 所有请求参数
* @Description: 统一下单接口
* @Return: org.jeecg.common.api.vo.Result
*/
@RequestMapping("createOrder")
public Result createOrder(@RequestBody Map<String, Object> data) {
try {
return Result.ok(wxOrderService.createOrder(data));
} catch (Exception ex) {
ex.printStackTrace();
return Result.error(ex.getMessage());
}
}
serviceImpl层
@Autowired
private WxPayService wxPayService;
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> createOrder(Map<String, Object> map) throws Exception {
String type = "paytest";//类型
String eventId = map.get("testid").toString();//测评id
String remark = map.get("title").toString();//测评标题
String customerId = map.get("openId") == null ? "" : map.get("openId").toString();//测评人id
String openId = map.get("openId").toString(); //用户微信标识
String description = map.get("title").toString();//商品描述
String amount = map.get("amount").toString();//付款金额
//保存微信订单
PayOrder wxOrder = new PayOrder();
wxOrder.setOpenId(openId);
wxOrder.setDescription(description);
wxOrder.setRemark(remark);
wxOrder.setEventId(eventId);
wxOrder.setEventModule("paytest");
wxOrder.setPayerTotal(Double.parseDouble(amount) / 100);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
StringBuffer sb = new StringBuffer();
sb.append(dateTimeFormatter.format(LocalDateTime.now()));
sb.append(new Random().nextInt(9999));
wxOrder.setOutTradeNo(sb.toString()); //时间戳+四位随机数
wxOrder.setCreateTime(new Date());
wxOrder.setTradeState(PayStatusConst.APPLY);
payOrderService.save(wxOrder);
//向微信服务器提交申请
return this.createWxOrderToWxService(openId, wxOrder.getOutTradeNo(), Integer.valueOf(amount), wxOrder.getDescription(), type);
}
/**
* @param openId 微信用户openId
* @param outTradeNo 我们的系统订单编号
* @param amount 付款金额
* @param description 商品描述
* @param type 付款类型 详见PayTypeEnumConst
* @Description: 向微信服务器发起预付款订单
* @Return: java.util.Map<java.lang.String, java.lang.Object>
*/
public Map<String, Object> createWxOrderToWxService(String openId, String outTradeNo, Integer amount, String description, String type) throws Exception {
final WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = WxPayUnifiedOrderRequest.newBuilder()
//调起支付的人的 openId
.openid(openId)
//订单编号
.outTradeNo(outTradeNo)
//订单金额
.totalFee(amount)
//商品描述
.body(description)
//获取本地IP
.spbillCreateIp(InetAddress.getLoopbackAddress().getHostAddress())
.attach(type)
//回调的 URL 地址 已经在WxConfig文件中进行设置
.build();
WxPayMpOrderResult result = this.wxPayService.createOrder(wxPayUnifiedOrderRequest);
Map<String, Object> returnMap = new HashMap<>();
returnMap.put("appId", result.getAppId());
returnMap.put("nonceStr", result.getNonceStr());
returnMap.put("packageValue", result.getPackageValue());
returnMap.put("paySign", result.getPaySign());
returnMap.put("signType", result.getSignType());
returnMap.put("timeStamp", result.getTimeStamp());
returnMap.put("orderId", outTradeNo);
return returnMap;
}
调用方法时的打印数据参考:

我这样写是因为在yml文件中设置了notifyUrl,然后在WxConfig文件中的wxService方法中进行支付配置了,如果不用配置也可以直接在使用wxPayService.createOrder()方法中对WxPayUnifiedOrderRequest参数设置.notifyUrl(回调地址),多种方式,实现即可。
异步回调方法为:
controller层:
@ApiOperation(value = "支付回调通知处理")
@RequestMapping("/notify")
public String parseOrderNotifyResult(@RequestBody String xmlData) throws WxPayException {
try {
this.wxOrderService.parseOrderNotifyResult(xmlData);
return WxPayNotifyResponse.success("成功");
} catch (Exception ex) {
ex.printStackTrace();
return WxPayNotifyResponse.failResp("失败");
}
}
serviceImpl层:
@Override
public void parseOrderNotifyResult(String xmlData) throws Exception {
final WxPayOrderNotifyResult notifyResult = this.wxPayService.parseOrderNotifyResult(xmlData);
if (null != notifyResult && StringUtils.isNotBlank(notifyResult.getTransactionId())) {
//更新用户订单状态
PayOrder wxOrder = payOrderService.getOne(new LambdaQueryWrapper<PayOrder>().eq(PayOrder::getOutTradeNo, notifyResult.getOutTradeNo()));
wxOrder.setBankType(notifyResult.getBankType());
wxOrder.setTradeState(PayStatusConst.SUCCESS);
wxOrder.setSuccessTime(new Date());
wxOrder.setTransactionId(notifyResult.getTransactionId());
payOrderService.updateById(wxOrder);
System.out.println("==============>atach" + notifyResult.getAttach());
if (StringUtils.isNotEmpty(notifyResult.getAttach())) {
}
}
}
注意:

除了这个方法,还有其他方式可用,实现即可,若是记录中有错误或遗漏的地方,欢迎评论指出。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
我正在尝试设置一个puppet节点,但rubygems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由rubygems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby
我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2
我不确定传递给方法的对象的类型是否正确。我可能会将一个字符串传递给一个只能处理整数的函数。某种运行时保证怎么样?我看不到比以下更好的选择:defsomeFixNumMangler(input)raise"wrongtype:integerrequired"unlessinput.class==FixNumother_stuffend有更好的选择吗? 最佳答案 使用Kernel#Integer在使用之前转换输入的方法。当无法以任何合理的方式将输入转换为整数时,它将引发ArgumentError。defmy_method(number)