一个PHP文件搞定微信H5支付

技术笔记 / 更新于 2019年9月5日 / 89 条评论

过年期间也坚持要撸码啊接着给博客除草,在这个小除夕是情人节的一天,祝大家新年快乐,情人节能够顺利脱单~~~回归正题,这篇文章介绍一下微信 H5 支付,以及单 PHP 文件完成微信 H5 支付。

什么是微信 H5 支付

H5 支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付

主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付

微信官方也提供了一个体验链接,请在微信外浏览器打开

开发流程

1、用户在商户侧完成下单,使用微信支付进行支付

2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型 trade_type=MWEB

3、统一下单接口返回支付相关参数给商户后台,如支付跳转 url(参数名 “mweb_url”),商户通过 mweb_url 调起微信支付中间页

4、中间页进行 H5 权限的校验,安全性检查(此处常见错误请见下文)

5、如支付成功,商户后台会接收到微信侧的异步通知

6、用户在微信支付收银台完成支付或取消支付,返回商户页面(默认为返回支付发起页面)

7、商户在展示页面,引导用户主动发起支付结果的查询

8、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用订单查询接口确认订单状态

9、展示最终的订单支付结果给用户

网上的对于微信 H5 支付的资源感觉少之又少,可能是因为微信 H5 支付出来时间不久吧,很多 PHP 微信支付接入教程都比较复杂,且需要配置和引入较多的文件,本人通过整理后给出一个单文件版的,希望可以给各位想接入微信 H5 支付的带来些许帮助和借鉴意义。以下为本篇文章的重点:

PHP 代码

<?php
/**
 * 微信H5支付PHP版本demo 部分代码来自网络
 * 作者:沈唁 
 * 博客:https://qq52o.me
 */
$money= 1;                     //充值金额 微信支付单位为分
$userip = get_client_ip();     //获得用户设备IP
$appid  = "";                  //应用APPID
$mch_id = "";                  //微信支付商户号
$key    = "";                 //微信商户API密钥
$out_trade_no = date('YmdHis').rand(1000,9999);//平台内部订单号
$nonce_str = createNoncestr();//随机字符串
$body = "H5充值";//内容
$total_fee = $money; //金额
$spbill_create_ip = $userip; //IP
$notify_url = "http://qq52o.me/wxpay/notify.php"; //回调地址
$trade_type = 'MWEB';//交易类型 具体看API 里面有详细介绍
$scene_info ='{"h5_info":{"type":"Wap","wap_url":"http://qq52o.me","wap_name":"支付"}}';//场景信息 必要参数
$signA ="appid=$appid&attach=$out_trade_no&body=$body&mch_id=$mch_id&nonce_str=$nonce_str&notify_url=$notify_url&out_trade_no=$out_trade_no&scene_info=$scene_info&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type";
$strSignTmp = $signA."&key=$key"; //拼接字符串  注意顺序微信有个测试网址 顺序按照他的来 直接点下面的校正测试 包括下面XML  是否正确
$sign = strtoupper(MD5($strSignTmp)); // MD5 后转换成大写
$post_data = "<xml>
                    <appid>$appid</appid>
                    <mch_id>$mch_id</mch_id>
                    <body>$body</body>
                    <out_trade_no>$out_trade_no</out_trade_no>
                    <total_fee>$total_fee</total_fee>
                    <spbill_create_ip>$spbill_create_ip</spbill_create_ip>
                    <notify_url>$notify_url</notify_url>
                    <trade_type>$trade_type</trade_type>
                    <scene_info>$scene_info</scene_info>
                    <attach>$out_trade_no</attach>
                    <nonce_str>$nonce_str</nonce_str>
                    <sign>$sign</sign>
            </xml>";//拼接成XML 格式
$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
$dataxml = postXmlCurl($post_data,$url); //后台POST微信传参地址  同时取得微信返回的参数 
$objectxml = (array)simplexml_load_string($dataxml, 'SimpleXMLElement', LIBXML_NOCDATA); //将微信返回的XML 转换成数组
function createNoncestr( $length = 32 ){
    $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
    $str ="";
    for ( $i = 0; $i < $length; $i++ )  {
        $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
    }
    return $str;
}
function postXmlCurl($xml,$url,$second = 30){
    $ch = curl_init();
    //设置超时
    curl_setopt($ch, CURLOPT_TIMEOUT, $second);
    curl_setopt($ch,CURLOPT_URL, $url);
    curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
    curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
    //设置header
    curl_setopt($ch, CURLOPT_HEADER, FALSE);
    //要求结果为字符串且输出到屏幕上
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
    //post提交方式
    curl_setopt($ch, CURLOPT_POST, TRUE);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
    //运行curl
    $data = curl_exec($ch);
    //返回结果
    if($data){
        curl_close($ch);
        return $data;
    }else{
        $error = curl_errno($ch);
        curl_close($ch);
        echo "curl出错,错误码:$error"."<br>";
    }
}
function get_client_ip($type = 0) {
    $type       =  $type ? 1 : 0;
    $ip         =   'unknown';
    if ($ip !== 'unknown') return $ip[$type];
    if($_SERVER['HTTP_X_REAL_IP']){//nginx 代理模式下,获取客户端真实IP
        $ip=$_SERVER['HTTP_X_REAL_IP'];
    }elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {//客户端的ip
        $ip     =   $_SERVER['HTTP_CLIENT_IP'];
    }elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {//浏览当前页面的用户计算机的网关
        $arr    =   explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $pos    =   array_search('unknown',$arr);
        if(false !== $pos) unset($arr[$pos]);
        $ip     =   trim($arr[0]);
    }elseif (isset($_SERVER['REMOTE_ADDR'])) {
        $ip     =   $_SERVER['REMOTE_ADDR'];//浏览当前页面的用户计算机的ip地址
    }else{
        $ip=$_SERVER['REMOTE_ADDR'];
    }
    // IP地址合法验证
    $long = sprintf("%u",ip2long($ip));
    $ip   = $long ? array($ip, $long) : array('0.0.0.0', 0);
    return $ip[$type];
}
?>

HTML 代码

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>微信支付</title>
 <style type="text/css">
 body{
 font-family: "Microsoft YaHei";
 }
 .pay-box{
 position: absolute;
 top: 50%;
 margin-top: -516px;
 left: 50%;
 margin-left: -320px;
 }
 .ico{
 width: 240px;
 height: 240px;
 border-radius: 120px;
 background: #3FB837;
 color: #fff;
 display: inline-block;
 font-size: 160px;
 line-height: 240px;
 }
 .txt{
 font-size: 42px;
 padding-top: 30px;
 color: #333;
 }
 .val{
 font-size: 80px;
 font-weight: bold;
 }
 .pay{
 width: 640px;
 height: 100px;
 margin-top: 100px;
 padding: 20px;
 border-radius: 10px;
 font-size:42px;
 color: #fff;
 background: #07BF05;
 border: 0px;
 text-align: center;
 }
 a{
 color: #fff;
 background: transparent !important;
 }
 </style>
</head>
<body>
 <div class="pay-box" style="text-align: center;">
 <div class="ico">
 ¥
 </div>
 <div class="txt">
 支付金额
 </div>
 <div class="val">
 ¥<span><?php echo $total_fee/100 ?></span> 
<!-- 这里使用原生PHP echo输出需要支付的价格 -->
 </div>
 <a class="pay" href="<?php echo $objectxml['mweb_url'] ?>"><button class="pay">确认支付</button></a> 
<!-- 这里点击调起微信支付页面 mweb_url  -->
 </div>
</body>
</html>

以上为微信 H5 支付 demo 的全部代码,其中 HTML 部分中的 mweb_url 是为拉起微信支付收银台的中间页面,可通过访问该 url 来拉起微信客户端,完成支付,mweb_url 的有效期为 5 分钟。

回调部分

因为微信支付相关回调代码基本一样,可参考 PHP 完成微信小程序在线支付功能一文中的回调代码,有什么问题可以联系我 QQ 或者评论留言。下文补充了同步回调

如何使用

标题说的就是单 PHP 文件完成微信支付,你可以把 HTML 代码写在 PHP 文件的后面,或者在 HTML 文件里面引入 PHP 文件,就可以使用了。


2018 年 3 月 21 日补充:

根据公司需求,需要一个同步回调页面,微信的支付是没有同步回调的,去查微信支付文档

正常流程用户支付完成后会返回至发起支付的页面,如需返回至指定页面,则可以在 MWEB_URL 后拼接上 redirect_url 参数,来指定回调页面。

我昨天想着是在生成 mweb_url 参数之前去拼接,结果证明是我想太多了!只能怪老板让加班到 8 点,我到 6 点就走了 , 直接在生成之后加上回调页面,文档读来读去也是这个意思,看来以后读文档真的要认真了。

PHP 部分:

需对 redirect_url 进行 urlencode 处理,将此部分代码加入到上面代码 $post_data 之前就行。

$returnUrl = "https://www.wechatpay.com.cn";
$return_Url = urlencode($returnUrl);

HTML 部分:

<a class="pay" href="<?php echo $objectxml['mweb_url'] ?>&redirect_url=<?php echo $return_Url; ?>"><button class="pay">确认支付</button></a>

通过统一下单接口获到的 MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096

则拼接后的地址为 MWEB_URL= https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx20161110163838f231619da20804912345&package=1037687096&redirect_url=https%3A%2F%2Fwww.wechatpay.com.cn

php支付宝H5支付(手机网站支付)

(官方文档 ---- https://docs.open.alipay.com/203/105285/)
第一步:创建应用
要在您的应用中接入电脑网站支付能力,您需要登录支付宝开放平台(open.alipay.com),在开发者中心中创建您的应用,应用审核通过后会生成应用唯一标识(APPID),并且可以申请开通开放产品使用权限。通过 APPID 您的应用才能调用开放产品的接口能力。需要详细了解开放平台创建应用步骤请参考《开放平台应用创建指南》。
 
第二步:配置应用
添加功能并签约
应用创建完成后,系统会自动跳转到应用详情页面。开发者可以在 功能列表 中点击 添加功能 来添加电脑网站支付功能。待应用上线后,您可以给添加的功能进行签约。电脑网站支付功能支持两种签约方式:商家中心签约和应用详情的功能列表处签约(如下图所示)。
详细步骤步骤可以参考添加应用功能,第三方应用可以代替商户签约。
配置密钥
 
为了保证交易双方(商户和支付宝)的身份和数据安全,开发者在调用接口前,需要配置双方密钥,对交易数据进行双方校验。密钥包含应用私钥(APP_PRIVATE_KEY)和应用公钥(APP_PUBLIC_KEY)。生成密钥后,开发者需要在开放平台开发者中心进行密钥配置,配置完成后可以获取支付宝公钥(ALIPAY_PUBLIC_KEY),配置的详细步骤请参考《配置应用环境》。您还可以通过观看快速签名教程学习密钥的配置。
配置公钥--查看下面地址
--https://docs.open.alipay.com/291/105971
 
说明:
支付宝开放平台 SDK 封装了签名和验签过程,只需配置账号及密钥参数,建议开发者使用。开发者还可以通过自助排查流程和验签教程自助排查配置应用过程中遇到的问题。
 
第三步:集成并配置 SDK(我是github一个文件搞定支付)
转载---- https://github.com/dedemao/alipay/blob/master/wap.php
<?php
header('Content-type:text/html; Charset=utf-8');
/*** 请填写以下配置信息 ***/
$appid = 'xxxxx';  //https://open.alipay.com 账户中心->密钥管理->开放平台密钥,填写添加了电脑网站支付的应用的APPID
$returnUrl = 'http://www.xxx.com/alipay/return.php';     //付款成功后的同步回调地址
$notifyUrl = 'http://www.xxx.com/alipay/notify.php';     //付款成功后的异步回调地址
$outTradeNo = uniqid();     //你自己的商品订单号
$payAmount = 0.01;          //付款金额,单位:元
$orderName = '支付测试';    //订单标题
$signType = 'RSA2';			//签名算法类型,支持RSA2和RSA,推荐使用RSA2
$rsaPrivateKey='xxxxx';		//商户私钥,填写对应签名算法类型的私钥,如何生成密钥参考:https://docs.open.alipay.com/291/105971和https://docs.open.alipay.com/200/105310
/*** 配置结束 ***/
$aliPay = new AlipayService();
$aliPay->setAppid($appid);
$aliPay->setReturnUrl($returnUrl);
$aliPay->setNotifyUrl($notifyUrl);
$aliPay->setRsaPrivateKey($rsaPrivateKey);
$aliPay->setTotalFee($payAmount);
$aliPay->setOutTradeNo($outTradeNo);
$aliPay->setOrderName($orderName);
$sHtml = $aliPay->doPay();
echo $sHtml;
class AlipayService
{
    protected $appId;
    protected $charset;
    protected $returnUrl;
    protected $notifyUrl;
    //私钥值
    protected $rsaPrivateKey;
    protected $totalFee;
    protected $outTradeNo;
    protected $orderName;
    public function __construct()
    {
        $this->charset = 'utf-8';
    }
    public function setAppid($appid)
    {
        $this->appId = $appid;
    }
    public function setReturnUrl($returnUrl)
    {
        $this->returnUrl = $returnUrl;
    }
    public function setNotifyUrl($notifyUrl)
    {
        $this->notifyUrl = $notifyUrl;
    }
    public function setRsaPrivateKey($rsaPrivateKey)
    {
        $this->rsaPrivateKey = $rsaPrivateKey;
    }
    public function setTotalFee($payAmount)
    {
        $this->totalFee = $payAmount;
    }
    public function setOutTradeNo($outTradeNo)
    {
        $this->outTradeNo = $outTradeNo;
    }
    public function setOrderName($orderName)
    {
        $this->orderName = $orderName;
    }
    /**
     * 发起订单
     * @return array
     */
    public function doPay()
    {
        //请求参数
        $requestConfigs = array(
            'out_trade_no'=>$this->outTradeNo,
            'product_code'=>'QUICK_WAP_WAY',
            'total_amount'=>$this->totalFee, //单位 元
            'subject'=>$this->orderName,  //订单标题
        );
        $commonConfigs = array(
            //公共参数
            'app_id' => $this->appId,
            'method' => 'alipay.trade.wap.pay',             //接口名称
            'format' => 'JSON',
            'return_url' => $this->returnUrl,
            'charset'=>$this->charset,
            'sign_type'=>'RSA2',
            'timestamp'=>date('Y-m-d H:i:s'),
            'version'=>'1.0',
            'notify_url' => $this->notifyUrl,
            'biz_content'=>json_encode($requestConfigs),
        );
        $commonConfigs["sign"] = $this->generateSign($commonConfigs, $commonConfigs['sign_type']);
        return $this->buildRequestForm($commonConfigs);
    }
    /**
     * 建立请求,以表单HTML形式构造(默认)
     * @param $para_temp 请求参数数组
     * @return 提交表单HTML文本
     */
    protected function buildRequestForm($para_temp) {
        $sHtml = "<form id='alipaysubmit' name='alipaysubmit' action='https://openapi.alipay.com/gateway.do?charset=".$this->charset."' method='POST'>";
		foreach($para_temp as $key=>$val){
            if (false === $this->checkEmpty($val)) {
                $val = str_replace("'","'",$val);
                $sHtml.= "<input type='hidden' name='".$key."' value='".$val."'/>";
            }		
		}
        //submit按钮控件请不要含有name属性
        $sHtml = $sHtml."<input type='submit' value='ok' style='display:none;''></form>";
        $sHtml = $sHtml."<script>document.forms['alipaysubmit'].submit();</script>";
        return $sHtml;
    }
    public function generateSign($params, $signType = "RSA") {
        return $this->sign($this->getSignContent($params), $signType);
    }
    protected function sign($data, $signType = "RSA") {
        $priKey=$this->rsaPrivateKey;
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        ($res) or die('您使用的私钥格式错误,请检查RSA私钥配置');
        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, version_compare(PHP_VERSION,'5.4.0', '<') ? SHA256 : OPENSSL_ALGO_SHA256); //OPENSSL_ALGO_SHA256是php5.4.8以上版本才支持
        } else {
            openssl_sign($data, $sign, $res);
        }
        $sign = base64_encode($sign);
        return $sign;
    }
    /**
     * 校验$value是否非空
     *  if not set ,return true;
     *    if is null , return true;
     **/
    protected function checkEmpty($value) {
        if (!isset($value))
            return true;
        if ($value === null)
            return true;
        if (trim($value) === "")
            return true;
        return false;
    }
    public function getSignContent($params) {
        ksort($params);
        $stringToBeSigned = "";
        $i = 0;
        foreach ($params as $k => $v) {
            if (false === $this->checkEmpty($v) && "@" != substr($v, 0, 1)) {
                // 转换成目标字符集
                $v = $this->characet($v, $this->charset);
                if ($i == 0) {
                    $stringToBeSigned .= "$k" . "=" . "$v";
                } else {
                    $stringToBeSigned .= "&" . "$k" . "=" . "$v";
                }
                $i++;
            }
        }
        unset ($k, $v);
        return $stringToBeSigned;
    }
    /**
     * 转换字符集编码
     * @param $data
     * @param $targetCharset
     * @return string
     */
    function characet($data, $targetCharset) {
        if (!empty($data)) {
            $fileType = $this->charset;
            if (strcasecmp($fileType, $targetCharset) != 0) {
                $data = mb_convert_encoding($data, $targetCharset, $fileType);
                //$data = iconv($fileType, $targetCharset.'//IGNORE', $data);
            }
        }
        return $data;
    }
}

PHP提取富文本字符串中的纯文本,并进行进行截取

/**
* 提取富文本字符串的纯文本,并进行截取;
* @param $string 需要进行截取的富文本字符串
* @param $int 需要截取多少位
*/
public static function StringToText($string,$num){
if($string){
//把一些预定义的 HTML 实体转换为字符
$html_string = htmlspecialchars_decode($string);
//将空格替换成空
$content = str_replace(” “, “”, $html_string);
//函数剥去字符串中的 HTML、XML 以及 PHP 的标签,获取纯文本内容
$contents = strip_tags($content);
//返回字符串中的前$num字符串长度的字符
return mb_strlen($contents,’utf-8′) > $num ? mb_substr($contents, 0, $num, “utf-8”).’….’ : mb_substr($contents, 0, $num, “utf-8”);
}else{
return $string;
}
}

php抽奖实现-概率算法

  1. <?php
  2. /*
  3. * 经典的概率算法,
  4. * $proArr是一个预先设置的数组,
  5. * 假设数组为:array(100,200,300,400),
  6. * 开始是从1,1000 这个概率范围内筛选第一个数是否在他的出现概率范围之内,
  7. * 如果不在,则将概率空间,也就是k的值减去刚刚的那个数字的概率空间,
  8. * 在本例当中就是减去100,也就是说第二个数是在1,900这个范围内筛选的。
  9. * 这样 筛选到最终,总会有一个数满足要求。
  10. * 就相当于去一个箱子里摸东西,
  11. * 第一个不是,第二个不是,第三个还不是,那最后一个一定是。
  12. * 这个算法简单,而且效率非常高,
  13. * 这个算法在大数据量的项目中效率非常棒。
  14. */
  15. function get_rand($proArr) {
  16. $result = ”;
  17. //概率数组的总概率精度
  18. $proSum = array_sum($proArr);
  19. //概率数组循环
  20. foreach ($proArr as $key => $proCur) {
  21. $randNum = mt_rand(1, $proSum);
  22. if ($randNum <= $proCur) {
  23. $result = $key;
  24. break;
  25. } else {
  26. $proSum -= $proCur;
  27. }
  28. }
  29. unset ($proArr);
  30. return $result;
  31. }
  32. /*
  33. * 奖项数组
  34. * 是一个二维数组,记录了所有本次抽奖的奖项信息,
  35. * 其中id表示中奖等级,prize表示奖品,v表示中奖概率。
  36. * 注意其中的v必须为整数,你可以将对应的 奖项的v设置成0,即意味着该奖项抽中的几率是0,
  37. * 数组中v的总和(基数),基数越大越能体现概率的准确性。
  38. * 本例中v的总和为100,那么平板电脑对应的 中奖概率就是1%,
  39. * 如果v的总和是10000,那中奖概率就是万分之一了。
  40. *
  41. */
  42. $prize_arr = array(
  43. ‘0’ => array(‘id’=>1,’prize’=>’平板电脑’,’v’=>1),
  44. ‘1’ => array(‘id’=>2,’prize’=>’数码相机’,’v’=>5),
  45. ‘2’ => array(‘id’=>3,’prize’=>’音箱设备’,’v’=>10),
  46. ‘3’ => array(‘id’=>4,’prize’=>’4G优盘’,’v’=>12),
  47. ‘4’ => array(‘id’=>5,’prize’=>’10Q币’,’v’=>22),
  48. ‘5’ => array(‘id’=>6,’prize’=>’下次没准就能中哦’,’v’=>50),
  49. );
  50. /*
  51. * 每次前端页面的请求,PHP循环奖项设置数组,
  52. * 通过概率计算函数get_rand获取抽中的奖项id。
  53. * 将中奖奖品保存在数组$res[‘yes’]中,
  54. * 而剩下的未中奖的信息保存在$res[‘no’]中,
  55. * 最后输出json个数数据给前端页面。
  56. */
  57. foreach ($prize_arr as $key => $val) {
  58. $arr[$val[‘id’]] = $val[‘v’];
  59. }
  60. $rid = get_rand($arr); //根据概率获取奖项id
  61. $res[‘yes’] = $prize_arr[$rid-1][‘prize’]; //中奖项
  62. unset($prize_arr[$rid-1]); //将中奖项从数组中剔除,剩下未中奖项
  63. shuffle($prize_arr); //打乱数组顺序
  64. for($i=0;$i<count($prize_arr);$i++){
  65. $pr[] = $prize_arr[$i][‘prize’];
  66. }
  67. $res[‘no’] = $pr;
  68. print_r($res);
  69. ?>

PHP非阻塞实现方法

为让 PHP 在后端处理长时间任务时不阻塞,快速响应页面请求,可以有如下措施:

1 使用 fastcgi_finish_request()

如果 PHP 与 Web 服务器使用了 PHP-FPM(FastCGI 进程管理器),那通过 fastcgi_finish_request() 函数能马上结束会话,而 PHP 线程可以继续在后台运行。

echo "program start...";

file_put_contents('log.txt','start-time:'.date('Y-m-d H:i:s'), FILE_APPEND);
fastcgi_finish_request();

sleep(1);
echo 'debug...';
file_put_contents('log.txt', 'start-proceed:'.date('Y-m-d H:i:s'), FILE_APPEND);

sleep(10);
file_put_contents('log.txt', 'end-time:'.date('Y-m-d H:i:s'), FILE_APPEND);

从输出结果可看到,页面打印完program start...,输出第一行到 log.txt 后会话就返回了,所以后面的 debug... 不会在浏览器上显示,而 log.txt 文件能完整地接收到三个完成时间。

2 使用 fsockopen()

使用 fsockopen() 打开一个网络连接或者一个Unix套接字连接,再用 stream_set_blocking() 非阻塞模式请求:

$fp = fsockopen("www.example.com", 80, $errno, $errstr, 30);

if (!$fp) {
    die('error fsockopen');
}

// 转换到非阻塞模式
stream_set_blocking($fp, 0);

$http = "GET /save.php  / HTTP/1.1\r\n";
$http .= "Host: www.example.com\r\n";
$http .= "Connection: Close\r\n\r\n";

fwrite($fp, $http);
fclose($fp);

3 使用 cURL

利用cURL中的 curl_multi_* 函数发送异步请求

$cmh = curl_multi_init();
$ch1 = curl_init();
curl_setopt($ch1, CURLOPT_URL, "http://localhost/");
curl_multi_add_handle($cmh, $ch1);
curl_multi_exec($cmh, $active);
echo "End\n";

4 使用 Gearman/Swoole 扩展

Gearman 是一个具有 php 扩展的分布式异步处理框架,能处理大批量异步任务。
Swoole 最近很火,有很多异步方法,使用简单。

5 使用缓存和队列

使用redis等缓存、队列,将数据写入缓存,使用后台计划任务实现数据异步处理。
这个方法在常见的大流量架构中应该很常见吧

6 调用系统命令

极端的情况下,可以调用系统命令,可以将数据传给后台任务执行,个人感觉不是很高效。

$cmd = 'nohup php ./processd.php $someVar >/dev/null  &';
`$cmd`

7 使用 pcntl_fork()

安装 pcntl 扩展,使用 pcntl_fork() 生成子进程异步执行任务,个人觉得是最方便的,但也容易出现僵尸进程。

$pid = pcntl_fork()
if ($pid == 0) {     child_func();    //子进程函数,主进程运行
} else {     father_func();   //主进程函数
}

echo "Process " . getmypid() . " get to the end.\n";   function father_func() {     echo "Father pid is " . getmypid() . "\n";
}

function child_func() {     sleep(6);     echo "Child process exit pid is " . getmypid() . "\n";     exit(0);
}

8 PHP 原生支持

外国佬的大招,没看懂
http://nikic.github.io/2012/12/22/Cooperative-multitasking-using-coroutines-in-PHP.html

PHP:计算小数点后位数

本文提供两种方法,一种将小数变成字符串,然后用”.“来截取,去.后的子字符串长度。还有一种是讲小数*10的N次方,比如10的8次方再对8次方取余数,再不断对10求余,直到对10求余的结果不为0。

相比来说,第一种方法要好不少,理由是这样的,比如1.000004在很边界的时候或者0.99999这样子PHP处理会有问题,会有误差,导致得到的位数不一样。而且精度难以把握,所以处理这种跟边界有关的问题最后能用字符串还是字符串来做。

第一种

private function _getFloatLength($num) {
$count = 0;
 
$temp = explode ( '.', $num );
 
if (sizeof ( $temp ) > 1) {
$decimal = end ( $temp );
$count = strlen ( $decimal );
}
 
return $count;
}

第二种

function getfloatlength($a){
	if(($a-(int)$a)<1E-9){
		$count=0;
	}
	$a=fmod(1E9*$a,1E9);
	while($a%10==0){
		if($a==0)break;
		$a=$a/10;
		$count--;
	}
	return count;
}

php生成随机红包

/**
     *   
     * @param $packet_total 红包总额 
     * @param $packet_count 红包个数 
     * @param $packet_max 每个小红包的最大额 
     * @param $packet_min 每个小红包的最小额 
     * @return 存放生成的每个小红包的值的一维数组 
     */
    public function getpacket() {

        $father_toal = $packet_total = input(‘packet_total’);
        $packet_count = input(‘packet_count’);
        $middle = $packet_count/2;   //用于判断用那种方法发红包
        //当已经分配了半数后最小值用$packet_min
        $packet_min = ($packet_total/$packet_count)*0.1;
        //当已经分配了小于半数时最小值用$packet_min1
        $packet_min1 = ($packet_total/$packet_count)*0.5;
        if($packet_min<0.01 && $packet_min1<0.01){
            return json_encode(array(‘status’=>0,’msg’=>”金额太少,红包数太多,小气鬼!”));
        }
        $n = $packet_count;
        for ($i = 0; $i < $packet_count – 1; $i++) {
            if($i <= $middle){
                $safe_total1 = (($packet_total – ($packet_count – $i) * $packet_min1) / ($packet_count – $i))*5; //随机安全上限  
                $money = mt_rand($packet_min1 * 100, $safe_total1 * 100) / 100;
                if($money<=0){
                    echo “hello”;break;
                }
                $result[] = round($money,2);
                $packet_total = $packet_total – $money;
            }else{
                $safe_total = ($packet_total – ($packet_count – $i) * $packet_min) / ($packet_count – $i); //随机安全上限  
                $money = mt_rand($packet_min * 100, $safe_total * 100) / 100;
                if($money<=0){
                    $money = 0.01;
                }
                $result[] = $money;
                $packet_total = $packet_total – $money;
            }
            
        }
        
        $left_money = $father_toal – array_sum($result);
        $result[] = $left_money;
         
        return json_encode($result);

    }

php使用正则替换过滤掉js(script)脚本例子

jquery中文网为您提供php使用正则替换过滤掉js(script)脚本例子等资源,欢迎您收藏本站,我们将为您提供最新的php使用正则替换过滤掉js(script)脚本例子资源利用php中的preg_replace正则匹配函数过滤掉网页中的js代码,preg_replace()中的第四个参数中表示替换的次数,默认是-1,表示替换全部;如果只想替换2次,可以写为 preg_replace($p1,$p2,$p3,2)。

匹配的规则不能用 “//<script.*<//script>//i”,因为它不能匹配到换行符,那么多行js就匹配不掉了。要用 “//<script[sS]*?<//script>//i”。里面的?表示尽可能少重复,也就是匹配最近的一个<//script>。

源码范例:

<?php

测试php正则匹配掉js代码 测试php正则匹配掉js代码 ‘; //测试php正则匹配掉js代码 $preg = “/

 header(“Content-type:text//html;charset=utf-8”);

 $str = ‘<script type=”text//javascript” src=”dd.js”><//script>

测试php正则匹配掉js代码<script type=”text//javascript” src=”123.js”><//script>

<script type=”text//javascript”>

 var aa = “sdsds”;

 alert(aa);

<//script>

测试php正则匹配掉js代码’;

 www.111Cn.net


 $preg = “//<script[sS]*?<//script>//i”;

 $newstr = preg_replace($preg,””,$str,3);    ////第四个参数中的3表示替换3次,默认是-1,替换全部

 echo $newstr;


?>

PHP 苹果支付校验

最近跟iOS这边对接苹果支付,商品等信息由前端访问谷歌服务器获取,支付等都由前端完成,后端则需要校验前端传过来的参数,通过后入库处理
苹果支付后返回给iOS的数据,有一串很长的base64的数据,后端只需要获取这串数据来校验

   /**IOS内购验证票据
     * @param string $receipt_data 付款后凭证
     * @param bool $sandbox 是否为沙盒
     * @param bool $transactions 仅对包含自动续订的应用收据使用此字段
     * @return mixed
     */
    public function validate_applepay($receipt_data,$sandbox=true,$transactions=false)
    {
        $jsonData = array('receipt-data' => $receipt_data, 'password' => config('apple_pay')['password'],'exclude-old-transactions'=>$transactions);
        $post_json = json_encode($jsonData);
        $url=$sandbox===true?"https://sandbox.itunes.apple.com/verifyReceipt":"https://buy.itunes.apple.com/verifyReceipt";
        $client=new Client(['verify' => false]);
        $response=   $client->post($url,[
            'body'=>$post_json,
            'headers'  => [
                'Content-Type' => 'application/json'
            ]
        ]);
        $result= $response->getBody()->getContents();;
        $res= json_decode($result, true);
     if (intval($res['status']) == 0) {  //验证成功
            $apple_pay = config('apple_pay');
            $products = array_keys($apple_pay['products']);
            $product_id = $res['receipt']['in_app'][0]['product_id'];
            if (!in_array($product_id, $products)) {
                $this->error='不是系统内部的商品ID';
                return false;
            }
            if ($res['receipt']['bundle_id']!= $apple_pay['bundle_id']) {
                $this->error='bundle_id 不一致';
                return false;
            }
            $info = $this->getOne($res['receipt']['in_app'][0]['transaction_id']);
            if ($info){
                $this->error='交易号重复';
                return false;
            }
            return true;
        } elseif (intval($res['status']) == 21007) {
            return  $this->validate_applepay($receipt_data,true);
        } else {
            $this->error=self::code[$res['status']];
            return false;
        }
    }

校验通过后,苹果返回的状态码

 Const code=[
        21000=>'App Store无法读取你提供的JSON数据',
        21002=>'收据数据不符合格式',
        21003=> '收据无法被验证',
        21004=> '你提供的共享密钥和账户的共享密钥不一致',
        21005=> '收据服务器当前不可用',
        21006=> '收据是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中',
        21007=>'收据信息是测试用(sandbox),但却被发送到产品环境中验证',
        21008=>'收据信息是产品环境中使用,但却被发送到测试环境中验证'
    ];

校验通过的返回的数据

    "receipt":{
        "receipt_type":"ProductionSandbox",
        "adam_id":0,
        "app_item_id":0,
        "bundle_id":"com.xxx.xxx",
        "application_version":"2",
        "download_id":0,
        "version_external_identifier":0,
        "receipt_creation_date":"2020-07-03 03:05:51 Etc/GMT",
        "receipt_creation_date_ms":"1593745551000",
        "receipt_creation_date_pst":"2020-07-02 20:05:51 America/Los_Angeles",
        "request_date":"2020-07-09 03:26:37 Etc/GMT",
        "request_date_ms":"1594265197186",
        "request_date_pst":"2020-07-08 20:26:37 America/Los_Angeles",
        "original_purchase_date":"2013-08-01 07:00:00 Etc/GMT",
        "original_purchase_date_ms":"1375340400000",
        "original_purchase_date_pst":"2013-08-01 00:00:00 America/Los_Angeles",
        "original_application_version":"1.0",
        "in_app":[
            {
                "quantity":"1",
                "product_id":"xxxx",
                "transaction_id":"1000000687969364",
                "original_transaction_id":"1000000687969364",
                "purchase_date":"2020-07-03 03:05:51 Etc/GMT",
                "purchase_date_ms":"1593745551000",
                "purchase_date_pst":"2020-07-02 20:05:51 America/Los_Angeles",
                "original_purchase_date":"2020-07-03 03:05:51 Etc/GMT",
                "original_purchase_date_ms":"1593745551000",
                "original_purchase_date_pst":"2020-07-02 20:05:51 America/Los_Angeles",
                "is_trial_period":"false"
            }]
    },
    "status":0,
    "environment":"Sandbox"
}

作者:seahonest
链接:https://www.jianshu.com/p/02a4fa66b9cf
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。