Creating a Payment Gateway
Introduction
- Registering the gateway
- Gateway backend settings
- Add support for recurring payments
- Set up the credit card form, if any
- Processing the payment
- Validating the payment
- Handling the Payment Gateway Callbacks
1) Registering the gateway
public function add_gateway( $gateways = array() ) { /** * $gateways[{GATEWAY_ID}] = array( * 'admin_label' => __( 'Custom Gateway', 'my-textdomain' ), // Gateway title to displayed in backend * 'checkout_label' => __( 'Custom Gateway', 'my-textdomain' ), // Gateway title to displayed in checkout * 'ordering' => 2 // Gateway display order * ); */ $gateways['2co'] = array( 'admin_label' => __( '2Checkout', 'wpinv-2co' ), 'checkout_label' => __( '2Checkout - Credit / Debit Card', 'wpinv-2co' ), 'ordering' => 2 ); return $gateways; } add_action( 'wpinv_payment_gateways', array( $this, 'add_gateway' ) );
2) Gateway backend settings
public function gateway_settings( $settings = array() ) { /** * $settings[{GATEWAY_ID}_FIELD_NAME] = array( * 'id' => {GATEWAY_ID}_FIELD_NAME, // Field name. * 'name' => __( 'Custom Field', 'my-textdomain' ), // Field title. * 'desc' => __( 'Description for custom field here.', 'my-textdomain' ), // Field description. * 'type' => 'text', // Field type. Ex: text, checkbox, select etc. * 'size' => 'large', // Field input size. Ex: small, medium, large. * 'std' => __( 'Default value', 'my-textdomain' ) // Field default value. * ); */ $settings['2co_desc'] = array( 'id' => '2co_desc', 'name' => __( 'Description', 'wpinv-2co' ), 'desc' => __( 'This controls the description which the user sees during checkout.', 'wpinv-2co' ), 'type' => 'text', 'size' => 'large', 'std' => __( 'Pay with your credit / debit card via 2Checkout gateway.', 'wpinv-2co' ) ); $settings['2co_sandbox'] = array( 'type' => 'checkbox', 'id' => '2co_sandbox', 'name' => __( '2Checkout Sandbox', 'wpinv-2co' ), 'desc' => __( '2Checkout sandbox can be used to test payments.', 'wpinv-2co' ), 'std' => 1 ); $settings['2co_vendor_id'] = array( 'type' => 'text', 'id' => '2co_vendor_id', 'name' => __( '2Checkout Account ID', 'wpinv-2co' ), 'desc' => __( 'Enter your 2Checkout account id. Example : 1303908', 'wpinv-2co' ), 'std' => '1303908', ); $settings['2co_ipn_url'] = array( 'type' => 'ipn_url', 'id' => '2co_ipn_url', 'name' => __( '2Checkout Instant Notification Url', 'wpinv-2co' ), 'desc' => __( 'Configure Instant Notification url at your 2Checkout account. Set this url to Instant Notification Settings at Notifications page and enable all notifications. If you have sandbox mode enabled then set it here as well Sandbox Notifications.', 'wpinv-2co' ), 'size' => 'large', 'custom' => '2co', 'readonly' => true, 'std' => wpinv_get_ipn_url( '2co' ), ); // Extra settings here return $settings; } add_filter( 'wpinv_gateway_settings_2co', array( $this->admin, 'gateway_settings' ) );
3) Add support for recurring payments
Enable accepting recurring payments
/** * add_filter( 'wpinv_{GATEWAY_ID}_support_subscription', '__return_true' ); */ add_filter( 'wpinv_2co_support_subscription', '__return_true' );
Disable accepting recurring payments
/** * add_filter( 'wpinv_{GATEWAY_ID}_support_subscription', '__return_false' ); */ add_filter( 'wpinv_2co_support_subscription', '__return_false' );
4) Set up the credit card form, if any
If gateway has any credit form fields
function wpinv_2co_cc_form() { // Your form fields here } add_action( 'wpinv_2co_cc_form', 'wpinv_2co_cc_form' );
If gateway does not have any credit form fields
/** * add_action( 'wpinv_{GATEWAY_ID}_cc_form', '__return_false' ); */ add_action( 'wpinv_2co_cc_form', '__return_false' );
5) Processing the payment
function wpinv_2co_process_payment( $purchase_data ) { if( ! wp_verify_nonce( $purchase_data['gateway_nonce'], 'wpi-gateway' ) ) { wp_die( __( 'Nonce verification has failed', 'wpinv-2co' ), __( 'Error', 'wpinv-2co' ), array( 'response' => 403 ) ); } // Collect payment data $payment_data = array( 'price' => $purchase_data['price'], 'date' => $purchase_data['date'], 'user_email' => $purchase_data['user_email'], 'invoice_key' => $purchase_data['invoice_key'], 'currency' => wpinv_get_currency(), 'items' => $purchase_data['items'], 'user_info' => $purchase_data['user_info'], 'cart_details' => $purchase_data['cart_details'], 'gateway' => '2co', 'status' => 'wpi-pending' ); // Record the pending payment $invoice = wpinv_get_invoice( $purchase_data['invoice_id'] ); if ( !empty( $invoice ) ) { $quantities_enabled = wpinv_item_quantities_enabled(); $invoice_id = $invoice->ID; $subscription_item = $invoice->get_recurring( true ); $is_recurring = !empty( $subscription_item ) ? true : false; // Is recurring payment? $params = array(); $params['sid'] = wpinv_get_option( '2co_vendor_id', false ); $params['mode'] = '2CO'; $params['currency_code'] = wpinv_get_currency(); $params['merchant_order_id'] = $invoice_id; $params['total'] = wpinv_sanitize_amount( $invoice->get_total() ); $params['key'] = $invoice->get_key(); $params['card_holder_name'] = $invoice->get_user_full_name(); $params['email'] = $invoice->get_email(); $params['street_address'] = wp_strip_all_tags( $invoice->get_address(), true ); $params['country'] = $invoice->country; $params['state'] = $invoice->state; $params['city'] = $invoice->city; $params['zip'] = $invoice->zip; $params['phone'] = $invoice->phone; $initial_amount = wpinv_format_amount( $invoice->get_total() ); $recurring_amount = wpinv_format_amount( $invoice->get_recurring_details( 'total' ) ); // Recurring payment $recurrence = ''; $duration = ''; if ( !empty( $is_recurring ) ) { $interval = $subscription_item->get_recurring_interval(); $period = $subscription_item->get_recurring_period(); $bill_times = (int)$subscription_item->get_recurring_limit(); $time_period = wpinv_2co_get_time_period( $interval, $period ); $recurrence = $time_period['interval'] . ' ' . $time_period['period']; $time_duration = $bill_times > 0 ? wpinv_2co_get_time_period( $bill_times, $period ) : array(); $duration = !empty($time_duration) ? $time_duration['interval'] . ' ' . $time_duration['period'] : 'Forever'; } $i = 0; // Items foreach ( $invoice->get_cart_details() as $item ) { $quantity = $quantities_enabled && !empty( $item['quantity'] ) && $item['quantity'] > 0 ? $item['quantity'] : 1; $params['li_' . $i . '_type'] = 'product'; $params['li_' . $i . '_name'] = $item['name']; $params['li_' . $i . '_quantity'] = $quantity; $params['li_' . $i . '_price'] = wpinv_sanitize_amount( $item['item_price'] ); $params['li_' . $i . '_tangible'] = 'N'; $params['li_' . $i . '_product_id'] = $item['id']; if ( $recurrence ) { $params['li_' . $i . '_recurrence'] = $recurrence; } if ( $duration ) { $params['li_' . $i . '_duration'] = $duration; } $i++; } $adjust = 0; // Discount if ( ( $discount = $invoice->get_discount() ) > 0 ) { $params['li_' . $i . '_type'] = 'coupon'; $params['li_' . $i . '_name'] = __( 'Discount', 'wpinv-2co' ); $params['li_' . $i . '_quantity'] = 1; $params['li_' . $i . '_price'] = wpinv_sanitize_amount( $discount ); $params['li_' . $i . '_tangible'] = 'N'; $params['li_' . $i . '_product_id'] = 'discount'; if ( $recurrence ) { $params['li_' . $i . '_recurrence'] = $recurrence; } if ( $is_recurring && $initial_amount != $recurring_amount ) { $params['li_' . $i . '_duration'] = $recurrence; $adjust = $recurring_amount - $initial_amount - $discount; } else { if ( $duration ) { $params['li_' . $i . '_duration'] = $duration; } } $i++; } // Tax if ( wpinv_use_taxes() && ( $tax = $invoice->get_tax() ) > 0 ) { if ( $adjust > 0 ) { $tax += $adjust; } $params['li_' . $i . '_type'] = 'tax'; $params['li_' . $i . '_name'] = __( 'Tax', 'wpinv-2co' ); $params['li_' . $i . '_quantity'] = 1; $params['li_' . $i . '_price'] = wpinv_sanitize_amount( $tax ); $params['li_' . $i . '_tangible'] = 'N'; $params['li_' . $i . '_product_id'] = 'tax'; if ( $adjust > 0 ) { $params['li_0_startup_fee'] = $adjust * -1; } if ( $recurrence ) { $params['li_' . $i . '_recurrence'] = $recurrence; } if ( $duration ) { $params['li_' . $i . '_duration'] = $duration; } } $params['purchase_step'] = 'payment-method'; $params['x_receipt_link_url'] = wpinv_get_ipn_url( '2co' ); $params = apply_filters( 'wpinv_2co_form_extra_parameters', $params, $invoice ); $redirect_text = __( 'Redirecting to 2Checkout site, click on button if not redirected.', 'wpinv-2co' ); $redirect_text = apply_filters( 'wpinv_2co_redirect_text', $redirect_text, $invoice ); // Empty the shopping cart wpinv_empty_cart(); ?>document.wpi_2co_form.submit();6) Validating the payment
function wpinv_2co_validate_checkout( $valid_data, $post ) { if ( !empty( $post['wpi-gateway'] ) && $post['wpi-gateway'] == '2co' ) { $invoice = wpinv_get_invoice_cart(); if ( empty( $invoice ) ) { return; } if ( !( (float)$invoice->get_total() > 0 ) ) { wpinv_set_error( 'empty_total', __( '2Checkout unable to process the payment with invoice total equal to zero or in a negative number.', 'wpinv-2co' ) ); return; } if ( $invoice->is_free_trial() ) { wpinv_set_error( 'empty_no_free_trial', __( '2Checkout payment gateway does not support free trial.', 'wpinv-2co')); return; } if ( $item = $invoice->get_recurring( true ) ) { if ( $item->get_recurring_period() == 'D' ) wpinv_set_error( 'empty_invalid_period', __( '2Checkout does not process the payment with recurring period in "Day". Can use only # Week, # Month, # Year.', 'wpinv-2co')); return; } } } add_action( 'wpinv_checkout_error_checks', 'wpinv_2co_validate_checkout', 11, 2 );7) Handling the Payment Gateway Callbacks
function wpinv_2co_process_ipn() { $request = wpinv_get_post_data( 'post' ); if ( empty( $request['message_type'] ) || empty( $request['vendor_id'] ) ) { die( '-1' ); } if ( empty( $request['sale_id'] ) || empty( $request['invoice_id'] ) || empty( $request['md5_hash'] ) ) { die( '-2' ); } if ( !wpinv_2co_validate_ipn( $request['md5_hash'], $request['sale_id'], $request['vendor_id'], $request['invoice_id'] ) ) { die( '-3' ); } $vendor_order_id = sanitize_text_field( $request['vendor_order_id'] ); $invoice = wpinv_get_invoice( $vendor_order_id ); if ( empty( $invoice ) ) { die( '-4' ); } $invoice_id = $invoice->ID; $invoice_status = isset($request['invoice_status']) ? $request['invoice_status'] : ''; $current_status = $invoice->status; $item_index = wpinv_2co_response_get_item_index( $request ); // Response contains main item. if ( empty( $item_index ) && strtoupper( $request['message_type'] ) != 'REFUND_ISSUED' ) { die( '-5' ); } switch( strtoupper( $request['message_type'] ) ) { case 'ORDER_CREATED' : if ($invoice_status == 'approved' || $invoice_status == 'deposited') { wpinv_update_payment_status( $invoice_id, 'publish' ); wpinv_set_payment_transaction_id( $invoice_id, $request['invoice_id'] ); wpinv_insert_payment_note( $invoice_id, sprintf( __( '2Checkout Sale id: %s, Invoice id: %s', 'wpinv-2co' ), $request['sale_id'], $request['invoice_id'] ) ); if ( !empty( $request['recurring'] ) ) { sleep(1); wpinv_2co_record_subscription_signup( $request, $invoice ); } } else if ($invoice_status == 'declined') { wpinv_update_payment_status( $invoice_id, 'wpi-failed' ); wpinv_record_gateway_error( __( '2CHECKOUT IPN ERROR', 'wpinv-2co' ), __( 'Payment failed due to invalid purchase found.', 'wpinv-2co' ), $invoice_id ); } else if ($invoice_status == 'pending') { wpinv_update_payment_status( $invoice_id, 'wpi-pending' ); } die( '1' ); break; case 'INVOICE_STATUS_CHANGED' : break; case 'REFUND_ISSUED' : $parent_invoice_id = $invoice_id; $invoice_id = wpinv_get_id_by_transaction_id( $request['invoice_id'] ); if ( empty( $invoice_id ) ) { $invoice_id = $parent_invoice_id; } if ( !empty( $request['recurring'] ) && !empty( $request['item_type_' . $item_index] ) && $request['item_type_' . $item_index] == 'refund' ) { wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); do_action( 'wpinv_2co_process_refund', $request, $invoice_id, 'item' ); wpinv_update_payment_status( $invoice_id, 'wpi-refunded' ); die( '1' ); } $refunded_amount = NULL; for ( $i = 1; $i <= (int)$request['item_count']; $i++ ) { $refunded_amount += $request['item_cust_amount_' . $i]; } if ( $refunded_amount !== NULL ) { $refunded_amount = wpinv_sanitize_amount( $refunded_amount ); } if ( $refunded_amount !== NULL && $refunded_amount get_total() ) { $refunded_amount = wpinv_price( $refunded_amount ); wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: Partial refund of %s processed. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $refunded_amount, $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); do_action( 'wpinv_2co_process_refund', $request, $invoice_id, 'partial' ); } else { wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); do_action( 'wpinv_2co_process_refund', $request, $invoice_id, 'full' ); wpinv_update_payment_status( $invoice_id, 'wpi-refunded' ); } die( '1' ); break; case 'RECURRING_INSTALLMENT_SUCCESS' : wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); wpinv_2co_record_subscription_payment( $request, $invoice ); die( '1' ); break; case 'RECURRING_INSTALLMENT_FAILED' : wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); die( '1' ); break; case 'RECURRING_STOPPED' : wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); $invoice->stop_subscription(); die( '1' ); break; case 'RECURRING_COMPLETE' : wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); $invoice->complete_subscription(); die( '1' ); break; case 'RECURRING_RESTARTED' : wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout: %s. Sale id: %s. Invoice id: %s', 'wpinv-2co' ), $request['message_description'], $request['sale_id'], $request['invoice_id'] ) ); $invoice->restart_subscription(); die( '1' ); break; case 'FRAUD_STATUS_CHANGED' : switch ( $request['fraud_status'] ) { case 'pass': wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout fraud review passed. Sale id: %s, Invoice id: %s', 'wpinv-2co' ), $request['sale_id'], $request['invoice_id'] ) ); break; case 'fail': wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout fraud review failed. Sale id: %s, Invoice id: %s', 'wpinv-2co' ), $request['sale_id'], $request['invoice_id'] ) ); break; case 'wait': wpinv_insert_payment_note( $invoice_id, wp_sprintf( __( '2Checkout fraud review in process. Sale id: %s, Invoice id: %s', 'wpinv-2co' ), $request['sale_id'], $request['invoice_id'] ) ); break; } die( '1' ); break; } do_action( 'wpinv_2co_process_ipn_type_' . strtolower( $request['message_type'] ), $request, $invoice ); die( '2' ); } add_action( 'wpinv_verify_2co_ipn', 'wpinv_2co_process_ipn' );