QualPay Checkout payment gateway

Implements a payment gateway for QualPay Checkout (secure order payment page) for WooCommerce. Works in both checkout place-order flow and my account order-pay flow.

Must be set to run on both front-end and back-end (everywhere).

add_action( 'woocommerce_init', function() {

	// Payment Gateway Class
	class WC_Gateway_QualPayCheckout extends WC_Payment_Gateway {

		// Constructor
		function __construct() {
			$this->endpoint = 'https://api.qualpay.com';
			$this->has_fields = false;
			$this->id = 'qualpaycheckout';
			$this->method_title = 'QualPay Checkout';
			$this->method_description = 'Implements QualPay Checkout Pay Page';
			$this->init_form_fields();
			$this->init_settings();
			$this->title = $this->get_option( 'title' );
			add_action(
				'woocommerce_update_options_payment_gateways_' . $this->id,
				[ $this, 'process_admin_options' ]
			);
		}

		// Settings Fields
		function init_form_fields() {
			$this->form_fields = array(
				'enabled' => array(
					'title' => __( 'Enable/Disable', 'woocommerce' ),
					'type' => 'checkbox',
					'label' => 'Enable QualPay Checkout',
					'default' => 'yes',
				),
				'testmode' => array(
					'title' => __( 'Test mode', 'woocommerce' ),
					'type' => 'checkbox',
					'label' => 'Enable test mode',
					'default' => 'yes',
				),
				'title' => array(
					'title' => __( 'Title', 'woocommerce' ),
					'type' => 'text',
				),
				'api_key' => array(
					'title' => 'QualPay API Key',
					'type' => 'text',
				),
				'checkout_id' => array(
					'title' => 'QualPay Checkout ID',
					'type' => 'number',
				),
				'currency_code' => array(
					'title' => 'QualPay Currency Code',
					'type' => 'number',
				),
				'profile_id' => array(
					'title' => 'QualPay Profile ID',
					'type' => 'number',
				),
			);
		}

		// WooCommerce Order Submitted
		function process_payment( $order_id ) {

			// Get Customer And Order
			$customer = WC()->session->get( 'customer' );
			$order = new WC_Order( $order_id );

			// Set Payent Pending Status
			$order->update_status( 'pending', __( 'Awaiting payment', 'woocommerce' ) );

			// Build Checkout Request
			$request = ( object ) [
				'amt_tran' => ( float ) $order->get_total(),
				'tran_currency' => ( string ) $this->settings['currency_code'],
				'purchase_id' => 'WC-' . $order_id,
				'profile_id' => ( string ) $this->settings['profile_id'],
				'merch_ref_num' => 'WC-' . $order_id,
				'customer_first_name' => $customer['first_name'],
				'customer_last_name' => $customer['last_name'],
				'customer_firm_name' => $customer['company'],
				'customer_email' => $customer['email'],
				'billing_addr1' => $customer['address_1'],
				'billing_addr2' => $customer['address_2'],
				'billing_state' => $customer['state'],
				'billing_city' => $customer['city'],
				'billing_zip' => $customer['postcode'],
				'moto_ecomm_ind' => '7',
				'preferences' => ( object ) [
					'success_url' => $this->get_return_url( $order ),
					'failure_url' => $order->get_checkout_payment_url(),
					'allow_partial_payments' => false,
					'allow_save_card' => false,
					'allow_ach_payment' => false,
					'email_receipt' => false,
					'request_type' => 'sale',
					'expire_in_secs' => 10000,
				],
				'checkout_profile_id' => $this->settings['checkout_id'],
			];
			$args = [
				'headers' => [
					'Authorization:Basic' => base64_encode( $this->settings['api_key'] . ':' ),
					'Content-Type' => 'application/json; charset=utf-8',
				],
				'data_format' => 'body',
				'body' => json_encode( $request )
			];

			// Send Checkout Request
			if( $this->settings['testmode'] == 'yes' ) {
				$this->endpoint = str_replace( 'api', 'api-test', $this->endpoint );
			}
			$url = $this->endpoint . '/platform/checkout';
			$response = wp_remote_post( $url, $args );

			// Parse Response
			$response = isset( $response['body'] ) ? json_decode( $response['body'] ) : $response;
			$message = isset( $response->message ) ? $response->message : $response;
			$data = isset( $response->data ) ? $response->data : '';
			$checkout_link = isset( $data->checkout_link ) ? $data->checkout_link : '';
			$checkout_id = isset( $data->checkout_id ) ? $data->checkout_id : '';

			// Handle Checkout Error
			if( ! $checkout_link ) {
				$message = sprintf(
					'Problem with QualPay Checkout: %s',
					print_r( $message, true )
				);
				$order->update_status( 'failed', $message );
				wc_add_notice( print_r( $message, true ), 'error' );
				return;
			}

			// Handle Checkout Success
			$order->add_order_note( 'Redirected to QP Checkout ID ' . $checkout_id );
			return [ 'result' => 'success', 'redirect' => $checkout_link ];
		}
	}
} );

// Add Gateway To WooCommerce
add_filter( 'woocommerce_payment_gateways', function( $methods ) {
	$methods[] = 'WC_Gateway_QualPayCheckout'; 
	return $methods;
} );

// Handle Double Querystring Bug
add_action( 'template_redirect', function() {
	$order_key = isset( $_GET['key'] ) ? esc_attr( $_GET['key'] ) : '';
	$rcode = isset( $_GET['rcode'] ) ? esc_attr( $_GET['rcode'] ) : '';
	if( strchr( $order_key, '?' ) ) {
		global $wp;
		$order_id = absint( $wp->query_vars['order-received'] );
		if( ! $order_id ) {
			$order_id = absint( $wp->query_vars['order-pay'] );
		}
		if( ! $order_id ) { return; }
		$order = new WC_Order( $order_id );
		if( is_wp_error( $order ) || ! $order ) { return; }
		$order_key = substr( $order_key, 0, strpos( $order_key, '?' ) );
		$redirect = empty( $_GET['pay_for_order'] )
			? $order->get_checkout_order_received_url() . '&rcode=' . $rcode
			: $order->get_checkout_payment_url() . '&rcode=' . $rcode;
		exit( wp_redirect( $redirect ) );
	}
} );

// Handle Payment Errors
add_action( 'template_redirect', function() {
	$rcode = isset( $_GET['rcode'] ) ? esc_attr( $_GET['rcode'] ) : '';
	if( $rcode == '000' || ! $rcode ) { return; }
	$rcodes = [
		'000' => 'Success',
		'005' => 'Simulation',
		100 => 'Bad request',
		101 => 'Invalid credentials',
		102 => 'Invalid pg_id',
		103 => 'Missing cardholder data',
		104 => 'Invalid transaction amount',
		105 => 'Missing auth_code',
		106 => 'Invalid AVS (Address Verification Service) data',
		107 => 'Invalid expiration date',
		108 => 'Invalid card number',
		109 => 'Field length validation failed',
		110 => 'Dynamic DBA not allowed',
		111 => 'Credits not allowed',
		112 => 'Invalid customer data',
		113 => 'Declined due to merchant risk settings',
		114 => 'Invalid currency code',
		115 => 'Invalid surcharge data',
		116 => 'Invalid email address',
		117 => 'Email address is required',
		118 => 'Invalid merchant logo URL',
		119 => 'Invalid ACH payment',
		401 => 'Void failed',
		402 => 'Refund failed',
		403 => 'Capture failed',
		404 => 'Batch close failed',
		405 => 'Tokenization failed',
		998 => 'Timeout',
		999 => 'Internal error',
	];
	if( empty( $rcodes[$rcode] ) ) {
		$rcodes[$rcode] = 'Missing error descriptor';
	}
	$error_message = sprintf(
		'QualPay Checkout error code %s: %s', $rcode, $rcodes[$rcode]
	);

	// Set Woo Order Status And Note
	$order_id = get_query_var( 'order-pay' );
	if( intval( $order_id ) ) {
		$order = new WC_Order( $order_id );
		if( ! is_wp_error( $order ) && $order ) {
			$order->update_status( 'failed' );
			$order->add_order_note( $error_message );
		}
	}

	// Set Woo Notice
	if( function_exists( 'wc_add_notice' ) ) {
		$message = sprintf(
			'
				Please try again.
				<a href="mailto:%s">Let us know if the problem persists.</a>
			',
			get_bloginfo( 'admin_email' )
		);
		wc_add_notice( $error_message . $message, 'error' );
	}
} );

// Handle Successful Payments
add_action( 'woocommerce_thankyou_qualpaycheckout', function( $order_id ) {

	// Get Order
	$order = new WC_Order( $order_id );
	if( is_wp_error( $order ) || ! $order ) { return; }

	// Security Check
	$order_key = isset( $_GET['key'] ) ? esc_attr( $_GET['key'] ) : '';
	if( $order_key != $order->get_order_key() ) { return; }

	// Return Code Check
	$rcode = isset( $_GET['rcode'] ) ? esc_attr( $_GET['rcode'] ) : '';
	if( $rcode != '000' ) { return; }

	// Empty The Cart
	global $woocommerce;
	$woocommerce->cart->empty_cart();

	// Set Order Note
	$order->add_order_note( 'QualPay Checkout payment success.' );

	// Mark Order Paid
	$order->payment_complete();
} );

How to use

  1. Log into a staging, development, or locally hosted clone of your site
  2. Install and activate Code Snippets
  3. WP Admin > Snippets > Add New
  4. Copy and paste the code from the Description tab above
  5. Check to ensure formatting came over properly and no syntax errors show up in the editor
  6. Customize the code as desired
  7. Add a meaningful title
  8. Select whether to run on front-end or back-end (or both)
  9. Click “Save and Activate”
  10. Test your site to ensure it works
  11. Disable if any problems, or recover
  12. Repeat for live environment

License

All code snippets are licensed GPLv2 (or later) matching WordPress licensing.

Disclaimer of warranty:

Except when otherwise stated in writing the copyright holders and/or other parties provide the program as-is without warranty of any kind, either expressed or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose.

Support

  1. Describe the issue and what you’ve observed.
  2. Describe your expected outcome(s).
  3. List steps to reproduce the issue.
  4. Optionally provide screen-shot or video URLs.
  5. Email to [email protected]

Partners

WP Engine - A smarter way to WordPress
The best email marketing tool, responsive templates, automations, Worldwide support, tracking and reports, Benchmark Email, free plan available
WP Engine - A smarter way to WordPress
Klaviyo partner badge
Okendo Partner, certified
WooCommerce, the most customizable eCommerce platform for building your online business. Click to get started.
Jetpack, a stronger, customizable site without sacrificing safety. Click to get started.