Files
wordpress-theme-aksrt/src/AKSRT/Payment/Gateways/YansongdaPayFactory.php
T

276 lines
7.2 KiB
PHP

<?php
/**
* Shared yansongda/pay config bootstrap for WeChat / Alipay gateways.
*
* @package AKSRT
*/
namespace AKSRT\Payment\Gateways;
defined('ABSPATH') || exit;
use Yansongda\Pay\Pay;
/**
* Builds Pay::config() payloads from aksrt_settings and boots the SDK.
*/
class YansongdaPayFactory {
/**
* Boot Pay with a partial channel config (wechat and/or alipay).
*
* @param array<string,mixed> $channels Keys: wechat?, alipay?
*/
public static function boot(array $channels): void {
$config = [
'logger' => self::logger_config(),
'http' => [
'timeout' => 10.0,
'connect_timeout' => 5.0,
],
];
if (!empty($channels['wechat']) && is_array($channels['wechat'])) {
$config['wechat'] = [
'default' => $channels['wechat'],
];
}
if (!empty($channels['alipay']) && is_array($channels['alipay'])) {
$config['alipay'] = [
'default' => $channels['alipay'],
];
}
// Re-config is safe; Pay caches tenant config per process.
Pay::config($config);
}
/**
* WeChat Pay API v3 merchant config.
*
* @return array<string,mixed>
*/
public static function wechat_from_settings(): array {
$all = self::settings();
$notify = home_url('/wp-json/aksrt/v1/payment/wechatpay/notify');
return [
'mch_id' => (string) ($all['wechat_merchantid'] ?? ''),
'mch_secret_key' => (string) ($all['wechat_api_v3_key'] ?? ''),
'mch_secret_cert' => (string) ($all['wechat_private_key_path'] ?? ''),
'mch_public_cert_path' => (string) ($all['wechat_cert_path'] ?? ''),
'mp_app_id' => (string) ($all['wechat_appid'] ?? ''),
'notify_url' => $notify,
'mode' => Pay::MODE_NORMAL,
];
}
/**
* Alipay cert-mode config (yansongda/pay 3.x requires cert paths for SN + verify).
*
* app_secret_cert may be PEM string or file path.
* Cert path fields accept absolute file paths (or PEM content via get_public_cert).
*
* @return array<string,mixed>
*/
public static function alipay_from_settings(): array {
$all = self::settings();
$notify = home_url('/wp-json/aksrt/v1/payment/alipay/notify');
$return = home_url('/wp-json/aksrt/v1/payment/alipay/return');
return [
'app_id' => (string) ($all['alipay_appid'] ?? ''),
'app_secret_cert' => (string) ($all['alipay_privatekey'] ?? ''),
'app_public_cert_path' => (string) ($all['alipay_app_public_cert_path'] ?? ''),
'alipay_public_cert_path' => (string) ($all['alipay_public_cert_path'] ?? ''),
'alipay_root_cert_path' => (string) ($all['alipay_root_cert_path'] ?? ''),
'return_url' => $return,
'notify_url' => $notify,
'mode' => Pay::MODE_NORMAL,
];
}
public static function boot_wechat(): void {
self::boot(['wechat' => self::wechat_from_settings()]);
}
public static function boot_alipay(): void {
self::boot(['alipay' => self::alipay_from_settings()]);
}
/**
* Convert Collection / ArrayAccess / array to plain array.
*
* @param mixed $data
* @return array<string,mixed>
*/
public static function to_array($data): array {
if (is_array($data)) {
return $data;
}
if ($data instanceof \Yansongda\Supports\Collection) {
return $data->all();
}
if ($data instanceof \ArrayAccess) {
$out = [];
foreach ($data as $k => $v) {
$out[$k] = is_array($v) || $v instanceof \ArrayAccess || $v instanceof \Yansongda\Supports\Collection
? self::to_array($v)
: $v;
}
return $out;
}
if (is_object($data) && method_exists($data, 'toArray')) {
$arr = $data->toArray();
return is_array($arr) ? $arr : [];
}
return [];
}
/**
* Nested value from array using dot path.
*
* @param array<string,mixed> $data
* @return mixed
*/
public static function dig(array $data, string $path, $default = null) {
$cur = $data;
foreach (explode('.', $path) as $seg) {
if (!is_array($cur) || !array_key_exists($seg, $cur)) {
return $default;
}
$cur = $cur[$seg];
}
return $cur;
}
/**
* Extract a redirect / payment URL from alipay web/wap response.
*
* @param mixed $result
*/
public static function extract_payment_url($result): string {
if (is_string($result) && $result !== '') {
return $result;
}
if ($result instanceof \Psr\Http\Message\ResponseInterface) {
$loc = $result->getHeaderLine('Location');
if ($loc !== '') {
return $loc;
}
$body = (string) $result->getBody();
if (preg_match('#action=[\'"]([^\'"]+)[\'"]#i', $body, $m)) {
return html_entity_decode($m[1], ENT_QUOTES);
}
if (preg_match('#https?://[^\s"\'<>]+#', $body, $m)) {
return $m[0];
}
// Auto-submit HTML form: callers may open as document; store empty payment_url and put HTML in payment_data.
return '';
}
if (is_object($result)) {
if (method_exists($result, 'getTargetUrl')) {
$url = (string) $result->getTargetUrl();
if ($url !== '') {
return $url;
}
}
if (method_exists($result, '__toString')) {
$s = (string) $result;
if (strpos($s, 'http') === 0) {
return $s;
}
}
}
$arr = self::to_array($result);
foreach (['payment_url', 'h5_url', 'code_url', 'mweb_url'] as $k) {
if (!empty($arr[$k]) && is_string($arr[$k])) {
return $arr[$k];
}
}
return '';
}
/**
* HTML body from alipay Response (form auto-submit) when no Location header.
*
* @param mixed $result
*/
public static function extract_html_body($result): string {
if ($result instanceof \Psr\Http\Message\ResponseInterface) {
if ($result->getHeaderLine('Location') !== '') {
return '';
}
return (string) $result->getBody();
}
return '';
}
/**
* Emit SDK success()/fail() ResponseInterface (or string) and exit.
*
* @param mixed $response
*/
public static function emit_response($response, string $fallback_body = 'success'): void {
if ($response instanceof \Psr\Http\Message\ResponseInterface) {
$status = $response->getStatusCode();
if ($status > 0 && !headers_sent()) {
http_response_code($status);
}
foreach ($response->getHeaders() as $name => $values) {
if (strtolower((string) $name) === 'transfer-encoding') {
continue;
}
foreach ($values as $value) {
header($name . ': ' . $value, false);
}
}
$body = (string) $response->getBody();
echo $body !== '' ? $body : $fallback_body;
exit;
}
if (is_string($response) && $response !== '') {
echo $response;
exit;
}
echo $fallback_body;
exit;
}
/**
* @return array<string,mixed>
*/
private static function settings(): array {
if (class_exists('\AKSRT\Settings\Settings')) {
return \AKSRT\Settings\Settings::get_all();
}
return [];
}
/**
* @return array<string,mixed>
*/
private static function logger_config(): array {
$enabled = defined('WP_DEBUG') && WP_DEBUG;
$file = '';
if ($enabled && function_exists('wp_upload_dir')) {
$uploads = wp_upload_dir();
if (empty($uploads['error']) && !empty($uploads['basedir'])) {
$dir = trailingslashit((string) $uploads['basedir']) . 'aksrt-pay';
if (!is_dir($dir)) {
wp_mkdir_p($dir);
}
$file = trailingslashit($dir) . 'pay.log';
}
}
return [
'enable' => $enabled && $file !== '',
'file' => $file,
'level' => 'info',
'type' => 'single',
'max_file' => 30,
];
}
}