Changeset 3347875
- Timestamp:
- 08/21/2025 06:00:47 AM (7 months ago)
- Location:
- allinpayintl/trunk
- Files:
-
- 2 edited
-
Allinpay_INTL.php (modified) (15 diffs)
-
readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
allinpayintl/trunk/Allinpay_INTL.php
r3339411 r3347875 6 6 * Author URI: http://rudrastyh.com 7 7 * Tested up to: 6.8.2 8 * Version: 1.1. 78 * Version: 1.1.8 9 9 */ 10 10 … … 62 62 add_filter('woocommerce_order_number', 'allinpay_woocommerce_order_number', 10, 2); 63 63 add_action('woocommerce_checkout_update_order_meta', 'allinpay_save_order_number'); 64 add_action('woocommerce_order_status_refunded', array($this, 'allinpay_process_refund')); 64 //add_action('woocommerce_order_status_refunded', array($this, 'allinpay_process_refund')); 65 add_action( 'allinpay_check_refund_status_hook', 'allinpay_check_refund_status_function', 10, 1 ); 66 add_action('woocommerce_order_refunded',array($this,'handle_api_refund_after_wc_refund'),10,2); 65 67 if ( !self::$button_added ) { 66 68 add_action('woocommerce_order_item_add_action_buttons',array($this, 'add_allinpay_query_button')); … … 73 75 add_action('wp_enqueue_scripts',array($this,'add_allinpay_style')); 74 76 77 75 78 $this->register_activation_hook(); 76 79 … … 89 92 $order_status = $order->get_status(); 90 93 // Display the button only if the order is in a specific status 91 if ($order_status === 'pending' || $order_status==='processing' ) {94 if ($order_status === 'pending' || $order_status==='processing' || $order_status==='completed') { 92 95 $button_text = __('Order Query', 'custom-payment-integration'); 93 96 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 94 97 $showInfo = '<button type="button" class="button" id="allinpay_query_btn" data-order-id="' . $order_id . '">' . $button_text . '</button>'; 95 98 $allowed_html=array( 96 'button'=> array(97 'type' => array(),98 'class' => array(),99 'id' => array(),100 'data-order-id' => array()101 )102 );99 'button'=> array( 100 'type' => array(), 101 'class' => array(), 102 'id' => array(), 103 'data-order-id' => array() 104 ) 105 ); 103 106 echo wp_kses($showInfo,$allowed_html); 104 107 } … … 107 110 function enqueue_allinpay_query_button_script($hook) 108 111 { 109 if ('post.php' !== $hook) { 112 // 确保在订单编辑页面加载脚本 113 if ('woocommerce_page_wc-orders' !== $hook) { 110 114 return; 111 115 } 112 116 113 117 $screen = get_current_screen(); 114 if ( 'shop_order' !== $screen->post_type) {118 if (!isset($screen->post_type) || 'shop_order' !== $screen->post_type) { 115 119 return; 116 120 } 117 wp_enqueue_script('allinpay-query-button-script', rtrim(plugin_dir_url(__FILE__),'/').'/js/custom-query-button.js', array('jquery'), '1.7', true); 121 122 //$this->allinpay_log('加载的页面为'.$hook); 123 //$this->allinpay_log('加载的方法为:'.$screen->post_type); 124 // 确保jQuery已经加载 125 wp_enqueue_script('jquery'); 126 127 // 加载查询按钮脚本 128 $script_url = rtrim(plugin_dir_url(__FILE__),'/').'/js/custom-query-button.js'; 129 wp_enqueue_script('allinpay-query-button-script', $script_url, array('jquery'), '1.7', true); 130 131 //$this->allinpay_log('加载的js路径为'.$script_url); 132 133 // 添加调试信息 134 135 // 本地化脚本变量 118 136 wp_localize_script( 'allinpay-query-button-script', 'custom_script_vars', array( 119 'nonce' => wp_create_nonce( 'allinpay_query' ),'ajaxurl'=> admin_url('admin-ajax.php') 120 ) ); 121 wp_enqueue_script( 'jquery' ); 122 } 123 124 function allinpay_query() 125 { 137 'nonce' => wp_create_nonce( 'allinpay_query' ), 138 'ajaxurl' => admin_url('admin-ajax.php') 139 ) ); 140 } 141 142 143 private function _api_query_request( $oriAccessOrderId ) { 144 if ( empty( $oriAccessOrderId ) ) { 145 return new WP_Error( 'missing_param', '查询失败:必须提供原始支付订单号。' ); 146 } 147 148 $this->allinpay_log( "核心查询方法: 准备查询订单号 {$oriAccessOrderId}" ); 149 150 $query_data = array( 151 'version' => $this->version, 152 'mchtId' => $this->merchant_id, 153 'transType' => 'Query', // 固定的接口类型 154 'oriAccessOrderId' => $oriAccessOrderId, 155 'signType' => 'RSA2' 156 ); 157 158 $signature = $this->generate_signature( $query_data ); 159 $query_data['sign'] = $signature; 160 161 $respResult = $this->request( $this->gateway_url, $query_data ); 162 $this->allinpay_log( "核心查询方法返回原始报文: " . $respResult ); 163 164 $parseJson = json_decode( $respResult, true ); 165 166 if ( ! $parseJson || ! isset( $parseJson['status'] ) ) { 167 $this->allinpay_log( "核心查询方法错误: 无法解析API响应或缺少状态信息。" ); 168 return new WP_Error( 'api_error', '查询失败:无法解析API响应或响应格式不正确。' ); 169 } 170 171 // 返回成功解析的JSON数组 172 return $parseJson; 173 } 174 175 function allinpay_query() { 126 176 check_ajax_referer( 'allinpay_query', 'nonce' ); 127 $this->allinpay_log('开始执行自定义查询操作'); 128 if (isset($_POST['order_id'])) { 129 $order_id = intval($_POST['order_id']); 130 $order = wc_get_order( $order_id ); 131 $query_data = array('version' => $this->version, 132 'mchtId'=> $this->merchant_id, 133 'transType'=> 'Query', 134 'oriAccessOrderId'=> $order->get_meta('custom_order_number'), 135 'signType' => 'RSA2' 136 ); 137 $signature = $this->generate_signature($query_data); 138 $query_data['sign'] = $signature; 139 $respResult = $this->request($this->gateway_url,$query_data); 140 $this->allinpay_log('查询接口返回的信息为:'.$respResult); 141 $parseJson = json_decode($respResult,true); 142 if($parseJson!=null){ 143 $status = $parseJson['status']; 144 if($status == 'PAIED'){ 145 $transaction_id = $parseJson['orderId']; 146 $order->payment_complete($transaction_id); 147 $order->update_status('completed',__('Payment complete!!!','woocommerce')); 148 wp_send_json_success('completed'); 149 }else if($status =='CLOSE' || $status=='REVOKED' || $status== 'COLSED'){ 150 $order->update_status('cancelled',__('order cancelled!!!','woocommerce')); 151 wp_send_json_success('cancelled'); 152 }else if($status == 'FAILED' ){ 153 $order->update_status('failed',__('order failed!!!','woocommerce')); 154 wp_send_json_success('failed'); 155 }else{ 156 wp_send_json_success('handler'); 177 $this->allinpay_log( '开始执行自定义AJAX订单查询操作' ); 178 179 if ( ! isset( $_POST['order_id'] ) ) { 180 wp_send_json_error( '无效的请求:缺少订单ID。' ); 181 wp_die(); 182 } 183 184 $order_id = intval( $_POST['order_id'] ); 185 $order = wc_get_order( $order_id ); 186 187 if ( ! $order ) { 188 wp_send_json_error( '查询失败:找不到指定的订单。' ); 189 wp_die(); 190 } 191 192 $oriAccessOrderId = $order->get_meta( 'custom_order_number' ); 193 if ( ! $oriAccessOrderId ) { 194 wp_send_json_error( '查询失败:找不到用于API查询的订单号。' ); 195 wp_die(); 196 } 197 198 // ... (您的API请求代码保持不变) ... 199 $parseJson = $this->_api_query_request( $oriAccessOrderId ); 200 $this->allinpay_log( 'AJAX查询接口返回的信息为:' . $parseJson ); 201 202 if ( ! $parseJson || ! isset( $parseJson['status'] ) ) { 203 wp_send_json_error( '查询失败:无法解析API响应或缺少状态信息。' ); 204 wp_die(); 205 } 206 207 $api_status = $parseJson['status']; 208 $current_wc_status = $order->get_status(); 209 210 switch ( $api_status ) { 211 // ... (case 'PAIED', 'CLOSE', 'FAILED' 等保持不变) ... 212 213 case 'PAIED': 214 if ( ! $order->is_paid() ) { 215 $transaction_id = $parseJson['orderId'] ?? $oriAccessOrderId; 216 $order->payment_complete( $transaction_id ); 217 $order->add_order_note( '通过API查询确认订单已支付。' ); 157 218 } 158 } 159 wp_die(); 160 } 161 wp_send_json_error('Invalid request.'); 219 wp_send_json_success( 'processing' ); // 通常支付完成后是 processing 220 break; 221 222 case 'CLOSE': 223 case 'REVOKED': 224 case 'COLSED': 225 if ( $current_wc_status !== 'cancelled' ) { 226 $order->update_status( 'cancelled', '通过API查询确认订单已关闭或撤销。' ); 227 } 228 wp_send_json_success( 'cancelled' ); 229 break; 230 231 case 'FAILED': 232 if ( $current_wc_status !== 'failed' ) { 233 $order->update_status( 'failed', '通过API查询确认订单支付失败。' ); 234 } 235 wp_send_json_success( 'failed' ); 236 break; 237 238 case 'REFUND': 239 // ---------------------------------------------------- 240 // 核心逻辑:当API只告知“有退款”时,我们相信WC自己的金额记录 241 // ---------------------------------------------------- 242 $order->add_order_note( 'API查询确认:网关记录此订单存在退款。系统将根据WooCommerce内部金额记录更新状态。' ); 243 244 $order_total = (float) $order->get_total(); 245 $refunded_total = (float) $order->get_total_refunded(); 246 247 // **金额检查** 248 if ( $refunded_total <= 0 ) { 249 // API说有退款,但WC记录里没有。这是一种状态不一致。 250 // 我们应该相信API,并提示用户可能需要手动同步。 251 $order->add_order_note( '警告:API表明有退款,但WooCommerce内部没有找到退款记录。可能需要手动处理。' ); 252 wp_send_json_success( 'needs-manual-check' ); // 返回自定义状态给前端 253 254 } elseif ( $refunded_total < $order_total ) { 255 // **部分退款** 256 // 确认订单状态不是 'refunded'。如果是,就纠正它。 257 if ( $current_wc_status === 'refunded' ) { 258 // 这是一个需要修复的错误状态 259 $order->update_status( 'completed', '状态纠正:根据部分退款金额,将状态从“已退款”恢复为“已完成”。' ); 260 wp_send_json_success( 'completed' ); // 返回修复后的状态 261 } else { 262 // 状态正确(比如 'completed'),无需改变 263 wp_send_json_success( $current_wc_status ); 264 } 265 266 } else { // $refunded_total >= $order_total 267 // **全额退款** 268 // 确保订单状态是 'refunded' 269 if ( $current_wc_status !== 'refunded' ) { 270 $order->update_status( 'refunded', '状态同步:根据全额退款金额,将订单状态更新为“已退款”。' ); 271 } 272 wp_send_json_success( 'refunded' ); 273 } 274 break; 275 276 default: 277 wp_send_json_success( 'handler' ); // 其他状态 278 break; 279 } 280 162 281 wp_die(); 163 282 } … … 171 290 // Payment gateway settings initialization 172 291 $this->form_fields = array( 173 'enabled' => array(174 'title' => __('Enable/Disable','allinpay_intl'),175 'type' => 'checkbox',176 'label' => 'Enable Allinpay INTL',177 'default' => 'no'178 ),179 'title' => array(180 'title' => 'Title',181 'type' => 'text',182 'description' => 'This controls the title which the user sees during checkout.',183 'default' => __('Allinpay INTL','allinpay_intl'),184 'desc_tip' => true185 186 ),187 'description' => array(188 'title' => 'Description',189 'type' => 'textarea',190 'description' => 'This controls the description which the user sees during checkout.',191 'default' => 'Pay with Allinpay INTL',192 ),193 'version' => array(194 'title' => 'Version',195 'type' => 'text',196 'description' => 'This controls the title which the user sees during checkout.',197 'default' => 'V2.0.0',198 'desc_tip' => true199 ),200 201 'trace_url' => array(202 'title' => 'Trace Url',203 'type' => 'text',204 'description' => 'Enter the trace url provided by Payment Gateway.',205 'default' => '',206 'desc_tip' => true207 ),208 'merchant_id' => array(209 'title' => 'Merchant ID',210 'type' => 'text',211 'description' => 'Enter your merchant ID provided by Payment Gateway.',212 'desc_tip' => true213 ),214 'private_key' => array(215 'title' => 'Private Key',216 'type' => 'textarea',217 'description' => 'Enter your private key provided by Payment Gateway.',218 'desc_tip' => true219 ),220 'public_key' => array(221 'title' => 'Public Key',222 'type' => 'textarea',223 'description' => 'Enter your public key provided by Payment Gateway.',224 'desc_tip' => true225 ),226 227 'debug_log' => array(228 'title' => __('debug_log','allinpay_intl'),229 'type' => 'checkbox',230 'description' => __('Log payment events,such as trade status,inside<code>wp-content/uploads/wc-logs/</code> ','allinpay_intl'),231 'default' => 'no'232 )233 234 );292 'enabled' => array( 293 'title' => __('Enable/Disable','allinpay_intl'), 294 'type' => 'checkbox', 295 'label' => 'Enable Allinpay INTL', 296 'default' => 'no' 297 ), 298 'title' => array( 299 'title' => 'Title', 300 'type' => 'text', 301 'description' => 'This controls the title which the user sees during checkout.', 302 'default' => __('Allinpay INTL','allinpay_intl'), 303 'desc_tip' => true 304 305 ), 306 'description' => array( 307 'title' => 'Description', 308 'type' => 'textarea', 309 'description' => 'This controls the description which the user sees during checkout.', 310 'default' => 'Pay with Allinpay INTL', 311 ), 312 'version' => array( 313 'title' => 'Version', 314 'type' => 'text', 315 'description' => 'This controls the title which the user sees during checkout.', 316 'default' => 'V2.0.0', 317 'desc_tip' => true 318 ), 319 320 'trace_url' => array( 321 'title' => 'Trace Url', 322 'type' => 'text', 323 'description' => 'Enter the trace url provided by Payment Gateway.', 324 'default' => '', 325 'desc_tip' => true 326 ), 327 'merchant_id' => array( 328 'title' => 'Merchant ID', 329 'type' => 'text', 330 'description' => 'Enter your merchant ID provided by Payment Gateway.', 331 'desc_tip' => true 332 ), 333 'private_key' => array( 334 'title' => 'Private Key', 335 'type' => 'textarea', 336 'description' => 'Enter your private key provided by Payment Gateway.', 337 'desc_tip' => true 338 ), 339 'public_key' => array( 340 'title' => 'Public Key', 341 'type' => 'textarea', 342 'description' => 'Enter your public key provided by Payment Gateway.', 343 'desc_tip' => true 344 ), 345 346 'debug_log' => array( 347 'title' => __('debug_log','allinpay_intl'), 348 'type' => 'checkbox', 349 'description' => __('Log payment events,such as trade status,inside<code>wp-content/uploads/wc-logs/</code> ','allinpay_intl'), 350 'default' => 'no' 351 ) 352 353 ); 235 354 } 236 355 … … 248 367 foreach($items as $item){ 249 368 $weldpay_item = array( 250 'sku' => $item['product_id'],251 'productName' => $item['name'],252 'price'=> $item['total'],253 'quantity'=> $item['quantity'],254 );369 'sku' => $item['product_id'], 370 'productName' => $item['name'], 371 'price'=> $item['total'], 372 'quantity'=> $item['quantity'], 373 ); 255 374 $weldpay_items[]=json_encode($weldpay_item); 256 375 } 257 376 //var_dump($weldpay_items); 258 377 $payment_data = array( 259 'version' => $this->version,260 'mchtId' => $this->merchant_id,261 'transType' => 'CashierPay',262 //'accessOrderId' => $order->get_id(),263 'accessOrderId' => $order->get_meta('custom_order_number'),264 'currency' => $order->get_currency(),265 'amount' => $order->get_total(),266 'language' =>substr(get_locale(), 0, 2),267 //'payPageStyle' => 'DEFAULT',268 'email' => $order->get_billing_email(),269 'returnUrl' => WC()->api_request_url('allinpay_front_payment_callback'),270 'notifyUrl' => WC()->api_request_url('allinpay_payment_callback'),271 //'timeZone' //持卡人时区,可选272 273 'signType' => 'RSA2',//签名类型274 //'txnTitle' => $order->get_title(),275 //'txnDetail' => $order->get_title(),276 277 278 'shippingFirstName' => !empty($order->get_shipping_first_name()) ? $order->get_shipping_first_name():$order->get_billing_first_name(),279 //'shippingFirstName' => $order->get_shipping_first_name(),280 'shippingLastName' => !empty($order->get_shipping_last_name()) ? $order->get_shipping_last_name():$order->get_billing_last_name(),281 'shippingAddress1' => !empty($order->get_shipping_address_1())? $order->get_shipping_address_1():$order->get_billing_address_1(),282 //'shippingAddress2' => $order->get_shipping_address_2(), //可选283 'shippingCity' => !empty($order->get_shipping_city())? $order->get_shipping_city():$order->get_billing_city(),284 //'shippingState'=> $order->get_shipping_state(),285 'shippingState'=>!empty($order->get_shipping_state()) ? $order->get_shipping_state() :( !empty($order->get_shipping_city())?$order->get_shipping_city():$order->get_billing_city()),286 'shippingCountry'=>!empty($order->get_shipping_country())?$order->get_shipping_country():$order->get_billing_country(),287 'shippingZipCode' => !empty($order->get_shipping_postcode())?$order->get_shipping_postcode():$order->get_billing_postcode(),288 'shippingPhone' => $order->get_billing_phone(),289 290 'billingFirstName' => $order->get_billing_first_name(),291 'billingLastName' => $order->get_billing_last_name(),292 'billingAddress1' => $order->get_billing_address_1(),293 //'billingAddress2' => $order->get_billing_address_2(),294 'billingCity' => $order->get_billing_city(),295 'billingState' => $order->get_billing_state(),296 'billingCountry' => $order->get_billing_country(),297 'billingZipCode' => $order->get_billing_postcode(),298 'billingPhone' => $order->get_billing_phone());378 'version' => $this->version, 379 'mchtId' => $this->merchant_id, 380 'transType' => 'CashierPay', 381 //'accessOrderId' => $order->get_id(), 382 'accessOrderId' => $order->get_meta('custom_order_number'), 383 'currency' => $order->get_currency(), 384 'amount' => $order->get_total(), 385 'language' =>substr(get_locale(), 0, 2), 386 //'payPageStyle' => 'DEFAULT', 387 'email' => $order->get_billing_email(), 388 'returnUrl' => WC()->api_request_url('allinpay_front_payment_callback'), 389 'notifyUrl' => WC()->api_request_url('allinpay_payment_callback'), 390 //'timeZone' //持卡人时区,可选 391 392 'signType' => 'RSA2',//签名类型 393 //'txnTitle' => $order->get_title(), 394 //'txnDetail' => $order->get_title(), 395 396 397 'shippingFirstName' => !empty($order->get_shipping_first_name()) ? $order->get_shipping_first_name():$order->get_billing_first_name(), 398 //'shippingFirstName' => $order->get_shipping_first_name(), 399 'shippingLastName' => !empty($order->get_shipping_last_name()) ? $order->get_shipping_last_name():$order->get_billing_last_name(), 400 'shippingAddress1' => !empty($order->get_shipping_address_1())? $order->get_shipping_address_1():$order->get_billing_address_1(), 401 //'shippingAddress2' => $order->get_shipping_address_2(), //可选 402 'shippingCity' => !empty($order->get_shipping_city())? $order->get_shipping_city():$order->get_billing_city(), 403 //'shippingState'=> $order->get_shipping_state(), 404 'shippingState'=>!empty($order->get_shipping_state()) ? $order->get_shipping_state() :( !empty($order->get_shipping_city())?$order->get_shipping_city():$order->get_billing_city()), 405 'shippingCountry'=>!empty($order->get_shipping_country())?$order->get_shipping_country():$order->get_billing_country(), 406 'shippingZipCode' => !empty($order->get_shipping_postcode())?$order->get_shipping_postcode():$order->get_billing_postcode(), 407 'shippingPhone' => $order->get_billing_phone(), 408 409 'billingFirstName' => $order->get_billing_first_name(), 410 'billingLastName' => $order->get_billing_last_name(), 411 'billingAddress1' => $order->get_billing_address_1(), 412 //'billingAddress2' => $order->get_billing_address_2(), 413 'billingCity' => $order->get_billing_city(), 414 'billingState' => $order->get_billing_state(), 415 'billingCountry' => $order->get_billing_country(), 416 'billingZipCode' => $order->get_billing_postcode(), 417 'billingPhone' => $order->get_billing_phone()); 299 418 $productInfo = '[' . implode(',', $weldpay_items) . ']'; 300 419 $payment_data['productInfo'] = $productInfo; … … 315 434 } 316 435 return array( 317 'result' => 'success',318 'redirect' => $parseJson['payUrl']319 );436 'result' => 'success', 437 'redirect' => $parseJson['payUrl'] 438 ); 320 439 } 321 440 // 将 iframe 插入页面,没啥用 … … 324 443 } 325 444 445 /** 446 * 在 WooCommerce 创建退款后,触发此函数来调用第三方 API。 447 * 448 * @param int $order_id 订单ID。 449 * @param int $refund_id 新创建的退款的ID。 450 */ 451 public function handle_api_refund_after_wc_refund( $order_id, $refund_id ) { 452 static $processed_refund_ids = array(); 453 if ( in_array( $refund_id, $processed_refund_ids ) ) { 454 $this->allinpay_log( "钩子重复触发:退款ID {$refund_id} 已在本次请求中处理,跳过。" ); 455 return; 456 } 457 $processed_refund_ids[] = $refund_id; 458 $this->allinpay_log( "开始处理退款ID {$refund_id} (由钩子触发)..." ); 459 $order = wc_get_order( $order_id ); 460 $refund = wc_get_order( $refund_id ); // 获取退款对象 461 462 if ( ! $order || ! $refund ) { 463 return; // 如果订单或退款无效,则退出 464 } 465 // 从退款对象中获取准确的退款金额 466 $amount_to_refund = $refund->get_amount(); 467 468 // 获取退款原因 469 $reason = $refund->get_reason() ? $refund->get_reason() : '由管理员操作的退款'; 470 471 // ---- 开始调用您的第三方API ---- 472 $platId = $order->get_meta( 'custom_order_number' ); 473 if ( ! $platId ) { 474 $order->add_order_note( '退款失败:无法找到用于API调用的 custom_order_number。' ); 475 return; 476 } 477 478 $refundOrderId = allinpay_order_id(); // 假设这是生成唯一退款ID的函数 479 480 $payment_data = array( 481 'version' => $this->version, 482 'mchtId' => $this->merchant_id, 483 'transType' => 'Refund', 484 'accessOrderId' => $refundOrderId, 485 'refundAmount' => $amount_to_refund, // 使用从退款对象获取的金额 486 'oriAccessOrderId' => $platId, 487 'signType' => 'RAS2', 488 ); 489 490 $signature = $this->generate_signature( $payment_data ); 491 $payment_data['sign'] = $signature; 492 493 $this->allinpay_log( '调用退款接口请求的参数为:' . print_r( $payment_data, true ) ); 494 $respResult = $this->request( $this->gateway_url, $payment_data ); 495 $this->allinpay_log( '退款接口返回的信息为:' . $respResult ); 496 497 $parseJson = json_decode( $respResult, true ); 498 499 // ---- 根据API响应更新订单备注 ---- 500 if ( $parseJson && isset($parseJson['resultCode']) ) { 501 502 if ( $parseJson['resultCode'] === '0000' ) { 503 // **情况一:同步成功 (小概率但需要处理)** 504 $order->add_order_note( sprintf( 505 '第三方网关退款已同步成功。金额: %s。退款交易ID: %s', 506 wc_price( $amount_to_refund ), 507 $refundOrderId 508 )); 509 // 标记退款已确认 510 $refund->update_meta_data( '_allinpay_refund_status', 'completed' ); 511 $refund->save(); 512 513 } elseif ( $parseJson['resultCode'] === 'P000' || $parseJson['resultCode'] === '0006' ) { 514 // **情况二:异步处理中 (核心逻辑)** 515 $order->add_order_note( sprintf( 516 '退款请求已提交至第三方网关,正在处理中。金额: %s。查询ID: %s。系统将在后台自动查询最终结果。', 517 wc_price( $amount_to_refund ), 518 $refundOrderId 519 )); 520 521 // 将查询所需的信息存入退款 meta 522 $refund->update_meta_data( '_allinpay_refund_status', 'pending_query' ); 523 $refund->update_meta_data( '_allinpay_refund_query_id', $refundOrderId ); 524 $refund->update_meta_data( '_allinpay_query_attempts', 0 ); // 初始化尝试次数 525 $refund->save(); 526 527 // **调度一个一次性的后台任务,在5分钟后执行查询** 528 wp_schedule_single_event( time() + ( 5 * MINUTE_IN_SECONDS ), 'allinpay_check_refund_status_hook', array( $refund_id ) ); 529 530 } else { 531 // **情况三:请求立即失败** 532 $error_desc = $parseJson['resultDesc'] ?? '未知错误'; 533 $order->add_order_note( sprintf( 534 '严重警告:退款请求被第三方网关拒绝。原因: %s。WooCommerce中的退款记录可能需要手动删除。', 535 $error_desc 536 )); 537 $refund->update_meta_data( '_allinpay_refund_status', 'failed' ); 538 $refund->save(); 539 } 540 } else { 541 // ... 处理API请求本身失败的情况 ... 542 $order->add_order_note( '严重警告:调用退款API失败,无法提交请求。请检查网络或配置。' ); 543 } 544 } 326 545 public function process_refund($order_id, $amount = null, $reason = ''){ 327 $order = new WC_Order ( $order_id );328 if(!$order){329 return new WP_Error( 'invalid_order', 'Invalid Order ID' );330 }331 if($order->get_status() !='completed' && $order->get_status()!='refunded'){332 return new WP_Error('invalid_order','Order status error,can not handler this');333 }334 $platId = $order->get_meta('custom_order_number');335 $refundOrderId = allinpay_order_id();336 if(!$platId){337 return new WP_Error('invalid_order','invalid customer order ID');338 }339 $payment_data = array(340 'version' => $this->version,341 'mchtId' => $this->merchant_id,342 'transType' => 'Refund',343 'accessOrderId' => $refundOrderId,344 'refundAmount' => $amount,345 'oriAccessOrderId'=> $platId,346 'signType' => 'RAS2'347 );348 349 $signature = $this->generate_signature($payment_data);350 $payment_data['sign'] = $signature;351 $this->allinpay_log('调用退款接口请求的参数为:'.$payment_data);352 $respResult = $this->request($this->gateway_url,$payment_data);353 $this->allinpay_log('退款接口返回的信息为:'.$respResult);354 $parseJson = json_decode($respResult,true);355 if($parseJson == null){356 $this->allinpay_log('解析退款接口返回的报文失败');357 return new WP_Error('order failed','refundOrderFailed');358 }359 if($parseJson['resultCode']!='0000'){360 return new WP_Error('order failed',$parseJson['resultDesc']);361 }362 546 return true; 363 547 } 364 548 /** 549 * [业务逻辑] WP-Cron 异步退款状态查询处理。 550 * 调用核心查询方法,并根据结果执行后台状态更新和修复。 551 * 552 * @param int $refund_id 要查询的退款的ID。 553 */ 554 public function allinpay_check_refund_status_function( $refund_id ) { 555 $refund = wc_get_order( $refund_id ); 556 if ( ! $refund ) { return; } 557 558 $order_id = $refund->get_parent_id(); 559 $order = wc_get_order( $order_id ); 560 561 if ( ! $order || $refund->get_meta( '_allinpay_refund_status' ) !== 'pending_query' ) { 562 return; 563 } 564 565 $oriAccessOrderId = $order->get_meta( 'custom_order_number' ); 566 567 // --- 调用通用核心查询方法 --- 568 $api_response = $this->_api_query_request( $oriAccessOrderId ); 569 570 // --- 处理核心方法返回的结果 --- 571 if ( is_wp_error( $api_response ) ) { 572 // API调用失败,记录日志并重新调度 573 $order->add_order_note( '警告:后台自动查询退款状态失败,将在稍后重试。原因:' . $api_response->get_error_message() ); 574 wp_schedule_single_event( time() + ( 30 * MINUTE_IN_SECONDS ), 'allinpay_check_refund_status_hook', array( $refund_id ) ); 575 return; 576 } 577 578 // API调用成功,根据返回的状态处理业务逻辑 579 $api_status = $api_response['status']; 580 $refundOrderId = $refund->get_meta('_allinpay_refund_query_id'); // 这是退款请求ID,用于日志 581 582 // 在Cron任务中,我们只关心API是否还是REFUND状态 583 if ( $api_status === 'REFUND' ) { 584 // 因为API不返回具体金额,我们无法100%确认是哪笔退款成功了。 585 // 但我们可以假设,既然发起了退款查询,这笔退款大概率是成功了。 586 // 这是一个基于现有API限制的合理推断。 587 $refund->update_meta_data( '_allinpay_refund_status', 'completed' ); 588 $order->add_order_note( sprintf( 589 '退款状态后台确认:网关状态为REFUND。假定退款请求 %s 已成功。', 590 $refundOrderId 591 )); 592 } else { 593 // 如果API返回了其他状态(例如PAID),说明退款可能失败了或被冲正。 594 // 这是需要人工干预的严重情况。 595 $refund->update_meta_data( '_allinpay_refund_status', 'failed' ); 596 $critical_note = sprintf( 597 '严重警告:退款失败!后台查询发现订单状态已不再是REFUND(当前为 %s)。退款请求 %s 可能已失败,请立即手动核查!', 598 $api_status, 599 $refundOrderId 600 ); 601 602 if ( $order->get_status() === 'refunded' ) { 603 $critical_note .= ' 订单状态已从“已退款”自动恢复为“已完成”。'; 604 $order->update_status( 'completed', '全额退款失败,状态自动恢复。' ); 605 } 606 $order->add_order_note( $critical_note ); 607 } 608 609 $refund->save(); 610 $order->save(); 611 } 365 612 private function toUrlParams(array $array, $isUrlEncode) 366 613 { … … 386 633 $paramsStr = $this->toUrlParams($array, true); 387 634 $args = array( 388 'body' => $paramsStr,389 'timeout' => '30',390 'redirection' => '30',391 'headers' =>array(392 'content-type'=> 'application/x-www-form-urlencoded;charset=utf-8'393 )394 );635 'body' => $paramsStr, 636 'timeout' => '30', 637 'redirection' => '30', 638 'headers' =>array( 639 'content-type'=> 'application/x-www-form-urlencoded;charset=utf-8' 640 ) 641 ); 395 642 $this->allinpay_log("发送到渠道的请求参数为:".$paramsStr); 396 643 $response = wp_remote_post($url,$args); … … 463 710 // 删除不需要参与签名的字段 464 711 unset( 465 $params['sign'],466 $params['wc-api']467 );712 $params['sign'], 713 $params['wc-api'] 714 ); 468 715 469 716 // 构建签名串 … … 481 728 // 根据回调参数进行相应的处理逻辑 482 729 if ($params['resultCode'] === '0000') { 483 // 更新订单状态为 已完成730 // 更新订单状态为处理中 484 731 $order = wc_get_order($order_id); 485 $order->payment_complete($transaction_id);486 732 $custom_data = array( 487 'localCurrency' => $params['localCurrency'], 488 'localAmount' => $params['localAmount'] 489 ); 490 733 'localCurrency' => $params['localCurrency'], 734 'localAmount' => $params['localAmount'] 735 ); 491 736 foreach ($custom_data as $key => $value) { 492 737 $order->update_meta_data($key, $value); 493 738 } 494 $order->update_status( 'completed', __( 'Payment complete!!!.', 'woocommerce' ) ); 495 $order->save(); 739 $order->payment_complete($transaction_id); 496 740 echo 'SUCCESS'; 497 741 wp_die(); 498 742 } else { 499 $custom_data = array( 500 'resultCode' => $params['resultCode'], 501 'resultDesc' => $params['resultDesc'] 502 ); 503 504 foreach ($custom_data as $key => $value) { 505 $order->update_meta_data($key, $value); 506 } 743 $order->update_meta_data('resultCode', $params['resultCode']); 744 $order->update_meta_data('resultDesc', $params['resultDesc']); 507 745 $order->update_status( 'failed', $params['resultDesc'] ); 508 746 $order->save(); … … 527 765 // 更新订单状态为处理中 528 766 $order = wc_get_order($order_id); 529 $order_status = $order->get_status(); 530 if($order->get_status() =='pending'){ 531 $order->update_status( 'processing', __( 'Payment processing.', 'woocommerce' ) ); 532 } 767 $order->add_order_note(__('Payment completed via Allinpay on front-end return.')); 533 768 // 处理支付成功后的逻辑,例如发送订单确认邮件、更新库存等 534 769 $redirect_url = $order->get_checkout_order_received_url(); … … 567 802 568 803 569 }570 571 572 function custom_payment_descriptions($description, $payment_id) {573 switch ($payment_id) {804 } 805 806 807 function custom_payment_descriptions($description, $payment_id) { 808 switch ($payment_id) { 574 809 case 'allinpay_intl': 575 810 $description .= '<p>This is the description for allInPay.</p>'; … … 578 813 default: 579 814 break; 580 }581 return $description;582 815 } 583 584 function allinpay_woocommerce_order_number($order_number, $order) 585 { 586 if ($order->get_date_created() instanceof WC_DateTime && $order->get_date_created()->getTimestamp() > time()) { 587 // 新订单,使用自定义订单号生成逻辑 588 $custom_order_number = gmdate('YmdHis').$order->get_id(); // 这里将订单号设置为 "MY-订单ID" 589 return $custom_order_number; 590 } 591 // 非新订单,保持原订单号不变 592 return $order_number; 816 return $description; 817 } 818 819 function allinpay_woocommerce_order_number($order_number, $order) 820 { 821 if ($order->get_date_created() instanceof WC_DateTime && $order->get_date_created()->getTimestamp() > time()) { 822 // 新订单,使用自定义订单号生成逻辑 823 $custom_order_number = gmdate('YmdHis').$order->get_id(); // 这里将订单号设置为 "MY-订单ID" 824 return $custom_order_number; 593 825 } 594 595 function allinpay_order_id(){ 596 $custom_order_number = gmdate('YmdHis'); // 自定义订单号生成逻辑 597 return 'RF'.$custom_order_number; 598 } 599 function allinpay_save_order_number($order_id) 600 { 601 $order = wc_get_order($order_id); 602 $custom_order_number = gmdate('YmdHis') . $order_id; // 自定义订单号生成逻辑 603 $order->update_meta_data('custom_order_number', $custom_order_number); 604 $order->save(); 605 } 606 607 $my_plugin = new Allinpay_INTL(); 826 // 非新订单,保持原订单号不变 827 return $order_number; 828 } 829 830 function allinpay_order_id(){ 831 $custom_order_number = gmdate('YmdHis'); // 自定义订单号生成逻辑 832 return 'RF'.$custom_order_number; 833 } 834 function allinpay_save_order_number($order_id) 835 { 836 $order = wc_get_order($order_id); 837 $custom_order_number = gmdate('YmdHis') . $order_id; // 自定义订单号生成逻辑 838 $order->update_meta_data('custom_order_number', $custom_order_number); 839 $order->save(); 840 } 841 842 $my_plugin = new Allinpay_INTL(); 608 843 609 844 } -
allinpayintl/trunk/readme.txt
r3339411 r3347875 5 5 Tested up to: 6.8.2 6 6 Requires PHP: 7.4 7 Stable tag: 1.1. 77 Stable tag: 1.1.8 8 8 License: GPLv2 or later 9 9 License URI: http://www.gnu.org/licenses/gpl-2.0.html … … 51 51 = 1.1.7= 52 52 fix some bugs 53 = 1.1.8= 54 Optimize the processing logic for PayNow refunds. 53 55 FAQ: 54 56 … … 70 72 71 73 == Upgrade Notice == 72 74 = 1.1.8= 75 Optimize the processing logic for PayNow refunds. 73 76 = 1.1.7= 74 77 fix some bugs
Note: See TracChangeset
for help on using the changeset viewer.