最近项目上线了一个小程序抢红包的迭代,其实小程序是目前是没有开放这个接口的,抢红包接口目前只对公众号开放,怎么实现的是不是大家很好奇呢?
首先,我们先来分析小程序抢红包功能的使用场景,在小程序里面微信付款,然后跳转到小程序红包分享页面,最后将该页面分享到微信群抢红包。
下图就是分享到微信群的小程序抢红包打开页面:
了解完使用场景之后,接下来是不是应该分析怎样去实现了?
我们先来理清这个使用流程,画一个简单的流程图来理解,能更清晰一点:
现在流程已经清楚了,我们再来分析分析抢红包这个看似简单的过程,可能会出现的问题(跟钱有关,得谨慎处理)?
分析来,分析去,除了支付和抢红包这两模块,其他的该怎样做还怎样做。
在这里先给大家填个坑,小程序虽然没有提供企业支付的sdk,但是你只需要明白一点,企业支付所需的openid只和当初申
请商户平台的appid有关,简单的说,就是小程序下申请的商户平台关联的openid也可完成企业支付。
(1)那就先来说说支付吧。就目前而言,微信支付已经做得很成熟了,有很多现成的第三方包,
直接集成到框架里面就可以用了【这里我啰嗦一句,集成的虽好,可不要贪杯澳】,有时间还是多研究研究官方的支付demo吧,这样在出现问题的时候才会如虎添翼。
这里,我想说的重点不是这个,关于小程序支付参考一下微信支付就可以了,要注意微信支付在付款成功之后注意回调接口,付款成功不一定是真正的支付成功,有时候存在网络或者其他一些不可控因素导致不是真正的支付成功。
(2)接下来,再来说说抢红包这个问题吧。看似一个简单的问题,这里面涉及到并发性问题,通俗点讲,红包就那么几个,可是抢红包的人成百上千(手慢无澳)。
l 利用redis的原子操作,将多线程理论上变成单线程,redis列表(红包生成)+队列(支付、发通知)来解决这个问题,一起来看看这种方案的具体实现。
项目中我使用的laravel框架,这里就用laravel来举例吧?
1、在小程序之后成功之后(发红包的人已将红包个数已设置好了,这里的红包金额是随机的),这个时候就会生成已经设置好金额和个数的红包列表。
**
*--------------------------------------------------------------------------
* wechatPay [ 微信支付,此处使用的是easywechat 4.*非官方包 ]
*--------------------------------------------------------------------------
* $config 小程序相关支付配置
* @Author Docker
*
*/
function wechatPay()
{ //小程序支付
$wechatPay = Factory::payment($config);
$result = $wechatPay->order->unify([
'body' => '-红包充值',
'out_trade_no' => $payment->out_trade_no,
'total_fee' => $payment->total_fee, //单位:分
'trade_type' => 'JSAPI',
'openid' => $openid
]);
$prepay_id = $result['prepay_id'];
$result = $wechatPay->jssdk->bridgeConfig($result['prepay_id'], false);
// 生成支付订单,订单状态为null
/* ........*/
}
/**
*--------------------------------------------------------------------------
* payCallBack [ 支付回调 ]
*--------------------------------------------------------------------------
* @Author Docker
*
* @return mixed
*/
function payCallBack()
{
$wechatPay = Factory::payment($config);
$response = $wechatPay->payment->handleNotify(
function ($notify, $successful) {
//使用通知里的 "微信支付订单号" 或者 "商户订单号" 去自己的数据库找到订单
$order = Payment::where('out_trade_no', $notify['out_trade_no'])->first();
if (!$order || $order->result_code === "SUCCESS") {
return true;
}
if ($notify['return_code'] === 'SUCCESS') {
$order->pay_time = Carbon::now()->toDayDateTimeString(); // 更新支付时间为当前时间
$order->status = 'PAID'; //支付成功,
$order->save();
/* ***************************/
//微信小程序支付成功后,生成红包
$this->inRedPackets($order);
/* ***************************/
} else {
$order->status = 'PAID_FAIL'; //待付款
$order->save();
}
return true;
});
return $response;
}
*/
function inRedPackets($packet)
{
//检查此红包是否已经生成红包记录
if (Redis::exists('BOUNCE_GENERATOR_' . $packet->unique_id)) {
return false;
}
$array = $this->generatePacket($packet->money, $packet->number, 100);
shuffle($array);
foreach ($array as $key => $value) {
Redis::lpush('BOUNCE_GENERATOR_' . $packet->unique_id, $value);
}
return true;
}
/**
*--------------------------------------------------------------------------
* generatePacket [ 红包金额算法 ]
*--------------------------------------------------------------------------
* @Author Docker
*
* @param $total_money
* @param $number
* @param $per
*/
function generatePacket($total_money ,$number, $per)
{
//不同红包类型,红包金额的生成方式不一样,此处使用随机生成
}
2、将红包转发到微信群之后,现在就开始抢红包,抢不完的24小时后退款,抢完了,给金主发小程序服务通知;
/**
*--------------------------------------------------------------------------
* grabRedPacket [ 抢红包并企业付款 ]
*--------------------------------------------------------------------------
* @Author Docker
*
* @param Request $request
* @return string
*/
function grabRedPacket($packet)
{ //是否领取过红包(同一个群、同一个人、同一份红包)
// 未领取过 查看是否已领取完 没领取过--抢红包
// 领取过 【提醒已领取过】
//显示该红包的领取记录
//未领取过--现在开始使用redis
$bounce_money = (int)(Redis::lpop('BOUNCE_GENERATOR_' . $packet->unique_id));
//企业付款
$job = (new \App\Jobs\Payment($bounce_money)->onQueue('PACKET.payment');
dispatch($job);
//抢完红包后,给金主发小程序服务消息
if (!Redis::exists('BOUNCE_GENERATOR_' . $packet->unique_id)) {
$job = (new \App\Jobs\PacketDrawedOver($packet->unique_id, $this->getAccessToken()))->onQueue('pushMessageWx')->delay(2);//推迟两秒执行
dispatch($job);
}
}
/**
*--------------------------------------------------------------------------
* enterprisePay [ 企业付款队列 ]
*--------------------------------------------------------------------------
* @Author Docker
*
*/
function enterprisePay()
{
$config = [
// 必要配置
'app_id' => 'xxxx',
'mch_id' => 'your-mch-id',
'key' => 'key-for-signature', // API 密钥
// 如需使用敏感接口(如退款、发送红包等)需要配置 API 证书路径(登录商户平台下载 API 证书)
'cert_path' => '/path/to/your/cert.pem', // XXX: 绝对路径!!!!
'key_path' => '/path/to/your/key', // XXX: 绝对路径!!!!
// 将上面得到的公钥存放路径填写在这里
'rsa_public_key_path' => '/path/to/your/rsa/publick/key/public-14339221228.pem', // <<<-----
'notify_url' => '默认的订单回调地址', // 你也可以在下单时单独设置来想覆盖它
];
$app = Factory::payment($config);
$result = $app->transfer->toBalance([
'partner_trade_no' => '1233455', // 商户订单号,需保持唯一性(只能是字母或者数字,不能包含有符号)
'openid' => 'oxTWIuGaIt6gTKsQRLau2M0yL16E',
'check_name' => 'FORCE_CHECK', // NO_CHECK:不校验真实姓名, FORCE_CHECK:强校验真实姓名
// 're_user_name' => '王小帅', 如果 check_name 设置为FORCE_CHECK,则必填用户真实姓名
'amount' => 10000, // 企业付款金额,单位为分
'desc' => '理赔', // 企业付款操作说明信息。必填
]);
if($response['result_code'] !== "SUCCESS") {
return "你支付失败啦";
}
//生成一条红包领取记录,一条插入语句,省略...
$packet_record = PacketRecord::create(企业付款相关数据);
return "支付成功啦";
}
哎呀,终于说完了,主要看思路。