<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use WeChatPay\Builder;
use WeChatPay\Crypto\AesGcm;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

class WeChatPayController extends Controller
{
  public static $mp_instance = false;
  public static $mp_config = false;

  public function callback($input, $header, $apiv3Key, $pem)
  {
    $inWechatpaySignature = $header['wechatpay-signature'][0];
    $inWechatpayTimestamp = $header['wechatpay-timestamp'][0];
    $inWechatpaySerial = $header['wechatpay-serial'][0];
    $inWechatpayNonce = $header['wechatpay-nonce'][0];
    $inBody = $input;
    $platformPublicKeyInstance = Rsa::from($pem, Rsa::KEY_TYPE_PUBLIC);
    $timeOffsetStatus = 300 >= abs(Formatter::timestamp() - (int)$inWechatpayTimestamp);
//    $verifiedStatus = Rsa::verify(
//      Formatter::joinedByLineFeed($inWechatpayTimestamp, $inWechatpayNonce, $inBody),
//      $inWechatpaySignature,
//      $platformPublicKeyInstance
//    );
//    if ($timeOffsetStatus && $verifiedStatus) {
    if ($timeOffsetStatus) {
      $inBodyArray = (array)json_decode($inBody, true);
      ['resource' => [
        'ciphertext' => $ciphertext,
        'nonce' => $nonce,
        'associated_data' => $aad
      ]] = $inBodyArray;
      $inBodyResource = AesGcm::decrypt($ciphertext, $apiv3Key, $nonce, $aad);
      return (array)json_decode($inBodyResource, true);
    } else {
      return false;
    }
  }

  public function builder($config)
  {
    self::$mp_config = $config;
    $merchantPrivateKeyFilePath = 'file://' . self::$mp_config['pem_path'];
    $platformCertificateFilePath = 'file://' . self::$mp_config['cer_path'];
    $merchantId = self::$mp_config['mchid'];
    $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);
    self::$mp_config['pem_key'] = $merchantPrivateKeyInstance;
    $merchantCertificateSerial = self::$mp_config['cer_num'];
    $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);
    $platformCertificateSerial = self::$mp_config['v3'];
    self::$mp_instance = Builder::factory([
      'mchid' => $merchantId,
      'serial' => $merchantCertificateSerial,
      'privateKey' => $merchantPrivateKeyInstance,
      'certs' => [
        $platformCertificateSerial => $platformPublicKeyInstance,
      ],
    ]);
  }

  public function refund($config)
  {
    $res = false;
    try {
      $resp = self::$mp_instance
        ->v3->refund->domestic->refunds
        ->post([
          'json' => [
            'transaction_id' => $config['transaction_id'],
            'out_refund_no' => $config['out_refund_no'],
            'amount' => [
              'refund' => $config['total'],
              'total' => $config['total'],
              'currency' => 'CNY',
            ],
          ],
        ]);
      $res = json_decode($resp->getBody(), true);
    } catch (\Exception $e) {
      if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        $res = json_decode($r->getBody(), true);
      }
    }
    return $res;
  }

  public function create($config)
  {
    $res = false;
    try {
      $post_data = [
        'appid' => self::$mp_config['appid'],
        'mchid' => self::$mp_config['mchid'],
        'description' => $config['description'],
        'out_trade_no' => $config['out_trade_no'],
        'notify_url' => $config['notify_url'],
        'amount' => [
          'total' => $config['total'],
        ],
        'payer' => [
          'openid' => $config['openid']
        ]
      ];
      $resp = self::$mp_instance
        ->v3->pay->transactions->jsapi
        ->post([
          'json' => $post_data,
        ]);
      $res = json_decode($resp->getBody(), true);
    } catch (\Exception $e) {
      if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        $res = json_decode($r->getBody(), true);
      }
    }
    $params = [
      'appId' => self::$mp_config['appid'],
      'timeStamp' => (string)time(),
      'nonceStr' => self::nonce(),
      'package' => 'prepay_id=' . $res['prepay_id'],
    ];
    $params += ['paySign' => Rsa::sign(
      Formatter::joinedByLineFeed(...array_values($params)),
      self::$mp_config['pem_key']
    ), 'signType' => 'RSA'];
    return [
      'appid' => $params['appId'],
      'timestamp' => $params['timeStamp'],
      'nonce_str' => $params['nonceStr'],
      'package' => $params['package'],
      'pay_sign' => $params['paySign'],
      'sign_type' => $params['signType'],
    ];
  }

  public function check($out_trade_no)
  {
    $res = false;
    try {
      $resp = self::$mp_instance
        ->v3->pay->transactions->outTradeNo->_out_trade_no_
        ->get([
          'query' => ['mchid' => self::$mp_config['mchid']],
          'out_trade_no' => (string)$out_trade_no,
        ]);
      $res = json_decode($resp->getBody(), true);
    } catch (\Exception $e) {
      if ($e instanceof \GuzzleHttp\Exception\RequestException && $e->hasResponse()) {
        $r = $e->getResponse();
        $res = json_decode($r->getBody(), true);
      }
    }
    return $res;
  }

  public static function nonce($l = 16)
  {
    $charts = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz0123456789";
    $max = strlen($charts) - 1;
    $noncestr = "";
    for ($i = 0; $i < $l; $i++) {
      $noncestr .= $charts[rand(0, $max)];
    }
    return $noncestr;
  }
}
