<?php
require_once 'WxPay.Api.php';
/**
 *
 * 刷卡支付实现类
 * 该类实现了一个刷卡支付的流程，流程如下：
 * 1、提交刷卡支付
 * 2、根据返回结果决定是否需要查询订单，如果查询之后订单还未变则需要返回查询（一般反复查10次）
 * 3、如果反复查询10订单依然不变，则发起撤销订单
 * 4、撤销订单需要循环撤销，一直撤销成功为止（注意循环次数，建议10次）
 *
 * 该类是微信支付提供的样例程序，商户可根据自己的需求修改，或者使用lib中的api自行开发，为了防止
 * 查询时hold住后台php进程，商户查询和撤销逻辑可在前端调用
 *
 * @author widy
 *
 */
class MicroPay
{
    /**
     *
     * 提交刷卡支付，并且确认结果，接口比较慢
     * @param WxPayMicroPay $microPayInput
     * @throws WxpayException
     * @return 返回查询接口的结果
     */
    public function pay($microPayInput)
    {
        //①、提交被扫支付
        $result = WxPayApi::micropay($microPayInput, 5);
        //如果返回成功
        if (
            !array_key_exists('return_code', $result) ||
            !array_key_exists('out_trade_no', $result) ||
            !array_key_exists('result_code', $result)
        ) {
            echo '接口调用失败,请确认是否输入是否有误！';
            throw new WxPayException('接口调用失败！');
        }

        //签名验证
        $out_trade_no = $microPayInput->GetOut_trade_no();

        //②、接口调用成功，明确返回调用失败
        if (
            $result['return_code'] == 'SUCCESS' &&
            $result['result_code'] == 'FAIL' &&
            $result['err_code'] != 'USERPAYING' &&
            $result['err_code'] != 'SYSTEMERROR'
        ) {
            return false;
        }

        //③、确认支付是否成功
        $queryTimes = 10;
        while ($queryTimes > 0) {
            $succResult = 0;
            $queryResult = $this->query($out_trade_no, $succResult);
            //如果需要等待1s后继续
            if ($succResult == 2) {
                sleep(2);
                continue;
            } elseif ($succResult == 1) {
                //查询成功
                return $queryResult;
            } else {
                //订单交易失败
                return false;
            }
        }

        //④、次确认失败，则撤销订单
        if (!$this->cancel($out_trade_no)) {
            throw new WxpayException('撤销单失败！');
        }

        return false;
    }

    /**
     *
     * 查询订单情况
     * @param string $out_trade_no  商户订单号
     * @param int $succCode         查询订单结果
     * @return 0 订单不成功，1表示订单成功，2表示继续等待
     */
    public function query($out_trade_no, &$succCode)
    {
        $queryOrderInput = new WxPayOrderQuery();
        $queryOrderInput->SetOut_trade_no($out_trade_no);
        $result = WxPayApi::orderQuery($queryOrderInput);

        if (
            $result['return_code'] == 'SUCCESS' &&
            $result['result_code'] == 'SUCCESS'
        ) {
            //支付成功
            if ($result['trade_state'] == 'SUCCESS') {
                $succCode = 1;
                return $result;
            }
            //用户支付中
            elseif ($result['trade_state'] == 'USERPAYING') {
                $succCode = 2;
                return false;
            }
        }

        //如果返回错误码为“此交易订单号不存在”则直接认定失败
        if ($result['err_code'] == 'ORDERNOTEXIST') {
            $succCode = 0;
        } else {
            //如果是系统错误，则后续继续
            $succCode = 2;
        }
        return false;
    }

    /**
     *
     * 撤销订单，如果失败会重复调用10次
     * @param string $out_trade_no
     * @param 调用深度 $depth
     */
    public function cancel($out_trade_no, $depth = 0)
    {
        if ($depth > 10) {
            return false;
        }

        $clostOrder = new WxPayReverse();
        $clostOrder->SetOut_trade_no($out_trade_no);
        $result = WxPayApi::reverse($clostOrder);

        //接口调用失败
        if ($result['return_code'] != 'SUCCESS') {
            return false;
        }

        //如果结果为success且不需要重新调用撤销，则表示撤销成功
        if ($result['result_code'] != 'SUCCESS' && $result['recall'] == 'N') {
            return true;
        } elseif ($result['recall'] == 'Y') {
            return $this->cancel($out_trade_no, ++$depth);
        }
        return false;
    }
}
