$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 */ 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 */ 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 */ 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 $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 */ private static function settings(): array { if (class_exists('\AKSRT\Settings\Settings')) { return \AKSRT\Settings\Settings::get_all(); } return []; } /** * @return array */ 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, ]; } }