Plugin Directory

Changeset 3347875


Ignore:
Timestamp:
08/21/2025 06:00:47 AM (7 months ago)
Author:
aipsg
Message:

tagging version 1.1.8

Location:
allinpayintl/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • allinpayintl/trunk/Allinpay_INTL.php

    r3339411 r3347875  
    66 * Author URI: http://rudrastyh.com
    77 * Tested up to: 6.8.2
    8  * Version: 1.1.7
     8 * Version: 1.1.8
    99 */
    1010
     
    6262            add_filter('woocommerce_order_number', 'allinpay_woocommerce_order_number', 10, 2);
    6363            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);
    6567            if ( !self::$button_added ) {
    6668                add_action('woocommerce_order_item_add_action_buttons',array($this, 'add_allinpay_query_button'));
     
    7375            add_action('wp_enqueue_scripts',array($this,'add_allinpay_style'));
    7476
     77
    7578            $this->register_activation_hook();
    7679
     
    8992            $order_status = $order->get_status();
    9093            // 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') {
    9295                $button_text = __('Order Query', 'custom-payment-integration');
    9396                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    9497                $showInfo = '<button type="button" class="button" id="allinpay_query_btn" data-order-id="' . $order_id . '">' . $button_text . '</button>';
    9598                $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                        );
    103106                echo wp_kses($showInfo,$allowed_html);
    104107            }
     
    107110        function enqueue_allinpay_query_button_script($hook)
    108111        {
    109             if ('post.php' !== $hook) {
     112            // 确保在订单编辑页面加载脚本
     113            if ('woocommerce_page_wc-orders' !== $hook) {
    110114                return;
    111115            }
    112116
    113117            $screen = get_current_screen();
    114             if ('shop_order' !== $screen->post_type) {
     118            if (!isset($screen->post_type) || 'shop_order' !== $screen->post_type) {
    115119                return;
    116120            }
    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            // 本地化脚本变量
    118136            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() {
    126176            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查询确认订单已支付。' );
    157218                    }
    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
    162281            wp_die();
    163282        }
     
    171290            // Payment gateway settings initialization
    172291            $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' => true
    185 
    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' => true
    199                 ),
    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' => true
    207                 ),
    208                 'merchant_id' => array(
    209                     'title' => 'Merchant ID',
    210                     'type' => 'text',
    211                     'description' => 'Enter your merchant ID provided by Payment Gateway.',
    212                     'desc_tip' => true
    213                 ),
    214                 'private_key' => array(
    215                     'title' => 'Private Key',
    216                     'type' => 'textarea',
    217                     'description' => 'Enter your private key provided by  Payment Gateway.',
    218                     'desc_tip' => true
    219                 ),
    220                 'public_key' => array(
    221                     'title' => 'Public Key',
    222                     'type' => 'textarea',
    223                     'description' => 'Enter your public key provided by  Payment Gateway.',
    224                     'desc_tip' => true
    225                 ),
    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                        );
    235354        }
    236355
     
    248367            foreach($items as $item){
    249368                $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                        );
    255374                $weldpay_items[]=json_encode($weldpay_item);
    256375            }
    257376            //var_dump($weldpay_items);
    258377            $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());
    299418            $productInfo =  '[' . implode(',', $weldpay_items) . ']';
    300419            $payment_data['productInfo'] = $productInfo;
     
    315434            }
    316435            return array(
    317                 'result' => 'success',
    318                 'redirect' => $parseJson['payUrl']
    319             );
     436                    'result' => 'success',
     437                    'redirect' => $parseJson['payUrl']
     438                    );
    320439        }
    321440        // 将 iframe 插入页面,没啥用
     
    324443        }
    325444
     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        }
    326545        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             }
    362546            return true;
    363547        }
    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        }
    365612        private function toUrlParams(array $array, $isUrlEncode)
    366613        {
     
    386633            $paramsStr = $this->toUrlParams($array, true);
    387634            $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                     );
    395642            $this->allinpay_log("发送到渠道的请求参数为:".$paramsStr);
    396643            $response = wp_remote_post($url,$args);
     
    463710            // 删除不需要参与签名的字段
    464711            unset(
    465                 $params['sign'],
    466                 $params['wc-api']
    467             );
     712                    $params['sign'],
     713                    $params['wc-api']
     714                 );
    468715
    469716            // 构建签名串
     
    481728            // 根据回调参数进行相应的处理逻辑
    482729            if ($params['resultCode'] === '0000') {
    483                 // 更新订单状态为已完成
     730                // 更新订单状态为处理中
    484731                $order = wc_get_order($order_id);
    485                 $order->payment_complete($transaction_id);
    486732                $custom_data = array(
    487                     'localCurrency' => $params['localCurrency'],
    488                     'localAmount' => $params['localAmount']
    489                 );
    490 
     733                        'localCurrency' => $params['localCurrency'],
     734                        'localAmount' => $params['localAmount']
     735                        );
    491736                foreach ($custom_data as $key => $value) {
    492737                    $order->update_meta_data($key, $value);
    493738                }
    494                 $order->update_status( 'completed', __( 'Payment complete!!!.', 'woocommerce' ) );
    495                 $order->save();
     739                $order->payment_complete($transaction_id);
    496740                echo 'SUCCESS';
    497741                wp_die();
    498742            } 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']);
    507745                $order->update_status( 'failed', $params['resultDesc'] );
    508746                $order->save();
     
    527765                // 更新订单状态为处理中
    528766                $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.'));
    533768                // 处理支付成功后的逻辑,例如发送订单确认邮件、更新库存等
    534769                $redirect_url = $order->get_checkout_order_received_url();
     
    567802
    568803
    569     }
    570 
    571 
    572     function custom_payment_descriptions($description, $payment_id) {
    573         switch ($payment_id) {
     804}
     805
     806
     807function custom_payment_descriptions($description, $payment_id) {
     808    switch ($payment_id) {
    574809        case 'allinpay_intl':
    575810            $description .= '<p>This is the description for allInPay.</p>';
     
    578813        default:
    579814            break;
    580         }
    581         return $description;
    582815    }
    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
     819function 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;
    593825    }
    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
     830function allinpay_order_id(){
     831    $custom_order_number = gmdate('YmdHis'); // 自定义订单号生成逻辑
     832    return 'RF'.$custom_order_number;
     833}
     834function 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();
    608843
    609844}
  • allinpayintl/trunk/readme.txt

    r3339411 r3347875  
    55Tested up to: 6.8.2
    66Requires PHP: 7.4
    7 Stable tag: 1.1.7
     7Stable tag: 1.1.8
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    5151= 1.1.7=
    5252fix some bugs
     53= 1.1.8=
     54Optimize the processing logic for PayNow refunds.
    5355FAQ:
    5456
     
    7072
    7173== Upgrade Notice ==
    72 
     74= 1.1.8=
     75Optimize the processing logic for PayNow refunds.
    7376= 1.1.7=
    7477fix some bugs
Note: See TracChangeset for help on using the changeset viewer.