<?php
/*
* This file is part of the overtrue/wechat.
*
* (c) overtrue <i@overtrue.me>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
/**
* Payment.php.
*
* @author overtrue <i@overtrue.me>
* @copyright 2015 overtrue <i@overtrue.me>
*
* @see https://github.com/overtrue
* @see http://overtrue.me
*/
namespace EasyWeChat\Payment;
use Doctrine\Common\Cache\Cache;
use EasyWeChat\Core\Exceptions\FaultException;
use EasyWeChat\Support\Url as UrlHelper;
use EasyWeChat\Support\XML;
use Overtrue\Socialite\AccessTokenInterface;
use Symfony\Component\HttpFoundation\Response;
class Payment
{
const SCHEME_PATH = 'weixin://wxpay/bizpayurl';
protected $api;
protected $merchant;
protected $cache;
public function __construct(Merchant $merchant, Cache $cache = null)
{
$this->merchant = $merchant;
$this->cache = $cache;
}
public function scheme($productId)
{
$params = [
'appid' => $this->merchant->app_id,
'mch_id' => $this->merchant->merchant_id,
'time_stamp' => time(),
'nonce_str' => uniqid(),
'product_id' => $productId,
];
$params['sign'] = generate_sign($params, $this->merchant->key, 'md5');
return self::SCHEME_PATH.'?'.http_build_query($params);
}
public function handleNotify(callable $callback)
{
$notify = $this->getNotify();
$signkey = $this->getAPI()->getSignkey(null);
if (!$notify->isValid($signkey)) {
throw new FaultException('Invalid request payloads.', 400);
}
$notify = $notify->getNotify();
$successful = 'SUCCESS' === $notify->get('result_code');
$handleResult = call_user_func_array($callback, [$notify, $successful]);
if (is_bool($handleResult) && $handleResult) {
$response = [
'return_code' => 'SUCCESS',
'return_msg' => 'OK',
];
} else {
$response = [
'return_code' => 'FAIL',
'return_msg' => $handleResult,
];
}
return new Response(XML::build($response));
}
public function handleRefundNotify(callable $callback)
{
$notify = $this->getRefundNotify()->getNotify();
$successful = 'SUCCESS' === $notify->get('return_code');
$handleResult = call_user_func_array($callback, [$notify, $successful]);
if (is_bool($handleResult) && $handleResult) {
$response = [
'return_code' => 'SUCCESS',
'return_msg' => 'OK',
];
} else {
$response = [
'return_code' => 'FAIL',
'return_msg' => $handleResult,
];
}
return new Response(XML::build($response));
}
public function handleScanNotify(callable $callback)
{
$notify = $this->getNotify();
$signkey = $this->getAPI()->getSignkey(null);
if (!$notify->isValid($signkey)) {
throw new FaultException('Invalid request payloads.', 400);
}
$notify = $notify->getNotify();
try {
$prepayId = call_user_func_array($callback, [$notify->get('product_id'), $notify->get('openid'), $notify]);
$response = [
'return_code' => 'SUCCESS',
'appid' => $this->merchant->app_id,
'mch_id' => $this->merchant->merchant_id,
'nonce_str' => uniqid(),
'prepay_id' => strval($prepayId),
'result_code' => 'SUCCESS',
];
$response['sign'] = generate_sign($response, $this->merchant->key);
} catch (\Exception $e) {
$response = [
'return_code' => 'SUCCESS',
'return_msg' => $e->getCode(),
'result_code' => 'FAIL',
'err_code_des' => $e->getMessage(),
];
}
return new Response(XML::build($response));
}
/**
* [WeixinJSBridge] Generate js config for payment.
*
* <pre>
* WeixinJSBridge.invoke(
* 'getBrandWCPayRequest',
* ...
* );
* </pre>
*
* @param string $prepayId
* @param bool $json
*
* @return string|array
*/
public function configForPayment($prepayId, $json = true)
{
$params = [
'appId' => $this->merchant->app_id,
'timeStamp' => strval(time()),
'nonceStr' => uniqid(),
'package' => "prepay_id=$prepayId",
'signType' => 'MD5',
];
$params['paySign'] = generate_sign($params, $this->merchant->key, 'md5');
return $json ? json_encode($params) : $params;
}
/**
* [JSSDK] Generate js config for payment.
*
* <pre>
* wx.chooseWXPay({...});
* </pre>
*
* @param string $prepayId
*
* @return array|string
*/
public function configForJSSDKPayment($prepayId)
{
$config = $this->configForPayment($prepayId, false);
$config['timestamp'] = $config['timeStamp'];
unset($config['timeStamp']);
return $config;
}
public function configForAppPayment($prepayId)
{
$params = [
'appid' => $this->merchant->app_id,
'partnerid' => $this->merchant->merchant_id,
'prepayid' => $prepayId,
'noncestr' => uniqid(),
'timestamp' => time(),
'package' => 'Sign=WXPay',
];
$params['sign'] = generate_sign($params, $this->merchant->key);
return $params;
}
public function configForShareAddress($accessToken, $json = true)
{
if ($accessToken instanceof AccessTokenInterface) {
$accessToken = $accessToken->getToken();
}
$params = [
'appId' => $this->merchant->app_id,
'scope' => 'jsapi_address',
'timeStamp' => strval(time()),
'nonceStr' => uniqid(),
'signType' => 'SHA1',
];
$signParams = [
'appid' => $params['appId'],
'url' => UrlHelper::current(),
'timestamp' => $params['timeStamp'],
'noncestr' => $params['nonceStr'],
'accesstoken' => strval($accessToken),
];
ksort($signParams);
$params['addrSign'] = sha1(urldecode(http_build_query($signParams)));
return $json ? json_encode($params) : $params;
}
public function setMerchant(Merchant $merchant)
{
$this->merchant = $merchant;
}
public function getMerchant()
{
return $this->merchant;
}
public function getNotify()
{
return new Notify($this->merchant);
}
public function getRefundNotify()
{
return new RefundNotify($this->merchant);
}
public function setAPI(API $api)
{
$this->api = $api;
}
public function getAPI()
{
return $this->api ?: $this->api = new API($this->getMerchant(), $this->cache);
}
public function __call($method, $args)
{
if (is_callable([$this->getAPI(), $method])) {
return call_user_func_array([$this->api, $method], $args);
}
}
}