<?php
/**
defined( ‘ABSPATH’ ) or die();
// Include the Payment Add-On framework.
GFForms::include_payment_addon_framework();
/**
class GF_AmazonPayfort extends GFPaymentAddOn {
/**
* Version of this add-on which requires reauthentication with the API.
*
* Anytime updates are made to this class that requires a site to reauthenticate Gravity Forms with Zoho, this
* constant should be updated to the value of GFForms::$version.
*
* @since 2.0.2
*
* @see GFForms::$version
*/
const LAST_REAUTHENTICATION_VERSION = '2.0';
/**
* Contains an instance of this class, if available.
*
* @since 1.0
* @access private
*
* @used-by GF_AmazonPayfort::get_instance()
*
* @var object $_instance If available, contains an instance of this class.
*/
private static $_instance = null;
/**
* Defines the version of the Amazon Payfort Add-On.
*
* @since 1.0
* @access protected
*
* @used-by GF_AmazonPayfort::scripts()
*
* @var string $_version Contains the version, defined from amazonpayfort.php
*/
protected $_version = GF_AMAZON_PAYFORT_VERSION;
/**
* Defines the minimum Gravity Forms version required.
*
* @since 1.0
* @access protected
*
* @var string $_min_gravityforms_version The minimum version required.
*/
protected $_min_gravityforms_version = '2.2';
/**
* Defines the plugin slug.
*
* @since 1.0
* @access protected
*
* @var string $_slug The slug used for this plugin.
*/
protected $_slug = 'gravityformsamazonpf';
/**
* Defines the main plugin file.
*
* @since 1.0
* @access protected
*
* @var string $_path The path to the main plugin file, relative to the plugins folder.
*/
protected $_path = 'amazon-payfort-gf/amazon-payfort-gf.php';
/**
* Defines the full path to this class file.
*
* @since 1.0
* @access protected
*
* @var string $_full_path The full path.
*/
protected $_full_path = __FILE__;
/**
* Defines the URL where this Add-On can be found.
*
* @since 1.0
* @access protected
*
* @var string $_url The URL of the Add-On.
*/
protected $_url = 'https://siyanaweb.com';
/**
* Defines the title of this Add-On.
*
* @since 1.0
* @access protected
*
* @var string $_title The title of the Add-On.
*/
protected $_title = 'Gravity Forms Amazon Payfort Add-On';
/**
* Defines the short title of the Add-On.
*
* @since 1.0
* @access protected
*
* @var string $_short_title The short title.
*/
protected $_short_title = 'Amazon Payfort';
/**
* Defines if Add-On should use Gravity Forms servers for update data.
*
* @since 1.0
* @access protected
*
* @var bool $_enable_rg_autoupgrade true
*/
protected $_enable_apf_autoupgrade = false;
/**
* Defines if user will not be able to create feeds for a form until a credit card field has been added.
*
* @since 1.0
* @since 2.0 set to false in favor of the Amazon Payfort field.
* @access protected
*
* @var bool $_requires_credit_card true.
*/
//protected $_requires_credit_card = false;
protected $_requires_credit_card = true;
/**
* Defines if callbacks/webhooks/IPN will be enabled and the appropriate database table will be created.
*
* @since 2.0
* @access protected
*
* @var bool $_supports_callbacks true
*/
protected $_supports_callbacks = true;
/**
* Defines the capability needed to access the Add-On settings page.
*
* @since 1.4.3
* @access protected
* @var string $_capabilities_settings_page The capability needed to access the Add-On settings page.
*/
protected $_capabilities_settings_page = 'gravityforms_amazonpayfort';
/**
* Defines the capability needed to access the Add-On form settings page.
*
* @since 1.4.3
* @access protected
* @var string $_capabilities_form_settings The capability needed to access the Add-On form settings page.
*/
protected $_capabilities_form_settings = 'gravityforms_amazonpayfort';
/**
* Defines the capability needed to uninstall the Add-On.
*
* @since 1.4.3
* @access protected
* @var string $_capabilities_uninstall The capability needed to uninstall the Add-On.
*/
protected $_capabilities_uninstall = 'gravityforms_amazonpayfort_uninstall';
/**
* Defines the capabilities needed for the Amazon Payfort Add-On
*
* @since 1.0
* @access protected
* @var array $_capabilities The capabilities needed for the Add-On
*/
protected $_capabilities = array( 'gravityforms_amazonpayfort', 'gravityforms_amazonpayfort_uninstall' );
/**
* Contains an instance of the Amazon Payfort API library, if available.
*
* @since 1.0
* @since 2.0 API contains only one instance for production, it is no longer an array.
* @access protected
* @var GF_AmazonPayfort_API $api If available, contains an instance of the Amazon Payfort API library.
*/
protected $api = null;
/**
* Contains the nonce that is used to verify 3DSecure success callback.
*
* @since 1.3
*
* @var string
*/
protected $success_nonce = null;
//protected $redirect_url = 'http://localhost/woocommerce/gf-post.php';
/**
* Get an instance of this class.
*
* @since 1.0
* @access public
*
* @uses GF_AmazonPayfort::$_instance
*
* @return GF_AmazonPayfort
*/
public static function get_instance() {
if ( null === self::$_instance ) {
self::$_instance = new self;
}
return self::$_instance;
}//end of member function
//No Use of this function
public function pre_init() {
add_filter( 'gform_is_delayed_pre_process_feed', array( $this, 'gform_is_delayed_pre_process_feed' ), 10, 4 );
add_filter( 'gform_entry_post_save', array( $this, 'gfamazonpayfort_entry_post_save' ), 11, 2 );
parent::pre_init();
//Added load on init to display the Amazon Payfort form
if (class_exists( 'GF_Field' ) ) {
require_once 'includes/class-gf-field-amazonpayfort-creditcard.php';
}
}//end of member function
public function feed_list_no_item_message() {
$settings = $this->get_plugin_settings();
if ( ! rgar( $settings, 'gf_amazonpayfort_configured' ) ) {
return sprintf( esc_html__( 'To get started, please configure your %sBorgun Settings%s!', 'gravityformsborgun' ), '<a href="' . admin_url( 'admin.php?page=gf_settings&subview=' . $this->_slug ) . '">', '</a>' );
} else {
return parent::feed_list_no_item_message();
}
}//end of function
/**
* Setup fields for feed settings.
*
* @since 1.0
* @access public
*
* @uses GFAddOn::remove_field()
* @uses GFFeedAddOn::feed_settings_fields()
*
* @return array $settings
*/
public function feed_settings_fields() {
// Get feed settings fields.
$settings = parent::feed_settings_fields();
//return GFRecurly_Feed_Settings_Fields::instance()->do_feed_settings_fields();
require_once 'amazon-payfort-gf/classes/class-gf-amazonpayfort-feed-settings-fields.php';
$settings = GFAmazonPayfort_Feed_Settings_Fields::instance()->do_feed_settings_fields();
return $settings;
// Prepare customer information fields.
/*
$settings = $this->add_field_before( 'recurringAmount', $subscription_name_field, $settings );
// Remove trial field.
$settings = $this->remove_field( 'trial', $settings );
return $settings;
*/
}//end of member function
/**
* Loads the Amazon Payfort field.
*
* @since 2.0
*
* @return void
*/
//Entry Post Save
public function gfamazonpayfort_entry_post_save( $entry, $form ) {
// echo "<pre>"; print_r($entry);
// echo "<pre>"; print_r($form); exit();
$amount_field_id = '';
$is_gfpayfort = false;
foreach($form['fields'] as $fields){
if($fields->type==='total'){
$amount_field_id = $fields->id;
continue;
}
}
foreach($entry as $key=>$single_entry){
if($single_entry === 'Amazon PayFort'){
$is_gfpayfort = true;
continue;
}
}
if(
$is_gfpayfort
&& (!in_array('subscribed', wp_get_current_user()->roles) && !in_array('gold_member', wp_get_current_user()->roles) && !in_array('platinum_member', wp_get_current_user()->roles))
//$this->has_amazonpayfort_card_field( $form )
){
require_once 'includes/class-gf-amazonpayfort-entry-post-save.php';
if( isset($_POST['input_5_2']) ) $item_price = substr( $_POST['input_5_2'], 1 );
if( isset($_POST['input_3_6_apf_cardNo']) ) $apf_cardNo = $_POST['input_3_6_apf_cardNo'];
if( isset($_POST['input_3_6_apf_expDate']) ) $apf_expDate = $_POST['input_3_6_apf_expDate'];
if( isset($_POST['input_3_6_apf_cvv']) ) $apf_cvv = $_POST['input_3_6_apf_cvv'];
if( isset($_POST['input_3']) ) $customer_email = $_POST['input_3'];
$merchant_reference = $entry["id"]; //Form Entry Id
$settings = $this->get_plugin_settings();
$merchant_identifier = $settings['merchant_identifier'];
$access_code = $settings['access_code'];
$shaRequestPhrase = $settings['sha_request_phrase'];
$shaResponsePhrase = $settings['sha_response_phrase'];
$command = 'PURCHASE'; //PURCHASE
$language = 'en';
$return_url = home_url('/') . 'wp-content/plugins/amazon-payfort-gf/amazon-payfort-return.php';
$source_url = $entry["source_url"];
if( $settings['apiMode'] == 'sandbox' )
$Url = 'https://sbcheckout.payfort.com/FortAPI/paymentPage'; //Sandbox
else
$Url = 'https://checkout.payfort.com/FortAPI/paymentPage'; //Production
$to = '[email protected]';
$subject = 'GF Entry Submission Content';
$body = json_encode($entry);
$headers = array('Content-Type: text/html; charset=UTF-8');
wp_mail( $to, $subject, $body, $headers );
$first_name = rgar( $entry, '371.3' );
$last_name = rgar( $entry, '371.6' );
if( !empty($first_name) )
$name = $first_name. ' '.$last_name;
else
$name = 'Itmam Customer';
if( empty($entry["373"]) )
$customer_email = '[email protected]';
else
$customer_email = $entry["373"];
$amount = $entry[$amount_field_id];
/* if( $entry["form_id"] == 1 ){ //Non-Disclosure Agreement - One Way
$amount = $entry["100"];
}elseif( $entry["form_id"] == 2 ){ //Promotional Agreement
$amount = $entry["65"];
}elseif( $entry["form_id"] == 3 ){ //Promotional Agreement
$amount = $entry["65"];
}elseif( $entry["form_id"] == 5 ){ //Non-Disclosure Agreement - One Way
$amount = $entry["115"];
}elseif( $entry["form_id"] == 7 ){ //Partnership Agreement
$amount = $entry["157"];
}elseif( $entry["form_id"] == 8 ){ //Non-Disclosure Agreement - Two Way
$amount = $entry["96"];
}elseif( $entry["form_id"] == 9 ){ //Share Sale & Purchase Agreement
$amount = $entry["202"];
}elseif( $entry["form_id"] == 10 ){
$amount = $entry["164"];
}elseif( $entry["form_id"] == 11 ){
$amount = $entry["164"];
}elseif( $entry["form_id"] == 13 ){ //Novation Agreement
$amount = $entry["198"];
}elseif( $entry["form_id"] == 15 ){ //Services Agreement
$amount = $entry["228"];
}elseif( $entry["form_id"] == 16 ){ //Business Licensing Agreement
$arr = explode( ' ', $entry["211.2"] );
$amount = $arr['0'];
}elseif( $entry["form_id"] == 20 ){ //Employment Agreement – Fixed Term (Non Renewable)
$amount = $entry["242"];
}elseif( $entry["form_id"] == 18 ){ //Employment Agreement – Fixed Term (Non Renewable)
$amount = $entry["242"];
}else if( $entry["form_id"] == 49 ){
$amount = $entry["242"];
}else if( $entry["form_id"] == 17 ){ //Employment Agreement – Fixed Term (Renewable)
$amount = $entry["211"];
}else if( $entry["form_id"] == 21 ){ //Assets Transfer Agreement
$amount = $entry["211"];
}else if( $entry["form_id"] == 35 ){ //مذكرة تفاهم Memorandum of Understanding
$amount = $entry["117"];
}else if( $entry["form_id"] == 37 ){ //اتفاقية استحقاق Entitlement Agreement
$amount = $entry["160"];
}else if( $entry["form_id"] == 50 ){ //Commercial Agency Agreement (Non-Exclusive)
$amount = $entry["298"];
}else if( $entry["form_id"] == 51 ){
$amount = $entry["350"];
}else if( $entry["form_id"] == 52 ){
$amount = $entry["335"];
}else{
$amount = $entry["350"];
} */
$requestParams = array(
'amount' => $amount * 100,
'currency' => 'SAR',
'merchant_identifier' => $merchant_identifier,
'access_code' => $access_code,
'merchant_reference' => $merchant_reference,
'customer_email' => $customer_email,
'customer_name' => $name,
'command' => $command,
'language' => $language,
'return_url' => $return_url,
);
$SHARequestPhrase = $settings['sha_request_phrase'];
$SHAResponsePhrase = $settings['sha_response_phrase'];
$SHAType = 'sha256';
$shaString = '';
ksort( $requestParams );
foreach ( $requestParams as $k => $v ) {
$shaString .= "$k=$v";
}//end of foreach loop
$shaString = $SHARequestPhrase . $shaString . $SHARequestPhrase;
$signature = hash( $SHAType, $shaString );
$requestParams['signature'] = $signature;
$apf_args_array = array();
foreach( $requestParams as $key => $value )
{
$apf_args_array[] = "<input type='hidden' name='$key' value='$value'/>";
}
$html_form = '
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<form action="' . $Url . '" method="post" id="apf_payment_form">'
. implode( '', $apf_args_array )
. ' <script type="text/javascript">
$("#apf_payment_form").submit();
</script>
</form>';
echo $html_form;
return GFAmazonPayFort_Entry_Post_Save::instance( $this )->gfamazonpayfort_entry_post_save( $entry, $form );
}
}//end of member function
// ====================================================================================================================================
/**
* This function handles the form confirmation by first trying to make the payment using paystation, then either returning the url
* to redirect the user's browser to so they see the payment screen, or the error message is returned.
* @param mixed $confirmation text or redirect for form submission
* @param array $form the form submission data
* @param array $entry the form entry
* @param bool $ajax form submission via AJAX
* @return mixed
*/
// ====================================================================================================================================
public function gformConfirmation( $confirmation, $form, $entry, $ajax ) {
if (in_array('subscribed', wp_get_current_user()->roles) || in_array('gold_member', wp_get_current_user()->roles) || in_array('platinum_member', wp_get_current_user()->roles)) {
return $confirmation;
}
// Return if not the current form.
if (RGForms::post('gform_submit') != $form['id']) {
return $confirmation;
}
// Get feed mapping form fields to payment request, return if not set as need the feed to actually do anything.
$feed = $this->getFeed($form['id']);
if (!$feed) {
return $confirmation;
}
// --------------------------------------
// Record payment gateway.
gform_update_meta( $entry['id'], 'payment_gateway', 'amazon-payfort-gf' );
// --------------------------------------
// Get the data posted from the form and build the payment request by creating a Paystation payment request object,
// setting it's properties, then calling the processPayment() method.
$formData = $this->getFormData($form);
$paymentReq = null;
// If a paystation override id has been specified on the form from dropdown to override etc then create new payment object with that Paystation id
// Otherwise default to the normal paystation_id in the main settings. This feature allows money to be paid in to different paystation accounts
// based on user selection of something on the form such as branch, region, country etc.
if ((isset($formData->PaystationOverrideId)) && ($formData->PaystationOverrideId)) {
$paymentReq = new GFPaystationPayment($formData->PaystationOverrideId, $this->options['gatewayId'], $this->options['testMode'], $this->securityHash);
}
else {
$paymentReq = new GFPaystationPayment($this->options['paystationId'], $this->options['gatewayId'], $this->options['testMode'], $this->securityHash);
}
$paymentReq->amount = (int) bcmul($formData->total, 100); // We multiply by 100 because the amount sent to the gateawy must be an int, so cents not a float.
$paymentReq->currency = GFCommon::get_currency(); // Get the currency from gravity forms.
$paymentReq->merchantSession = $this->buildSessionId($entry['id']); // Call function in this class to create the merchantSession id.
$paymentReq->merchantReference = $formData->MerchantReference;
$paymentReq->customerDetails = $formData->CustomerDetails;
$paymentReq->orderDetails = $formData->OrderDetails;
// --------------------------------------
// Try making the payment, if successful the redirect the user to the payment URL, if not then catch error.
try {
$response = $paymentReq->processPayment(); // Call processPayment function in PaystationPayment class, it will do the POST to the paystation gateway.
// If digitalOrder is populated then there were no issues with the transaction request, so update the status of the
// the lead record and then set the confirmation to the url of the payment screen.
if ($response->digitalOrder) {
GFFormsModel::update_lead_property($entry['id'], 'payment_status', 'Processing');
GFFormsModel::update_lead_property($entry['id'], 'transaction_id', $response->transactionId);
// NB: GF handles redirect via JavaScript if headers already sent, or AJAX.
$confirmation = array('redirect' => $response->digitalOrder);
}
else {
// There was an error with the payment initiation, an error message will be set so
// throw it so that the exception code below will update the status to failed
// and cause the user to see the error emssage.
throw new GFPaystationException($response->errorMessage);
}
} catch (GFPaystationException $e) {
// Update the status to failed.
GFFormsModel::update_lead_property($entry['id'], 'payment_status', 'Failed');
// If the there is a failure url set the confirmation to the failure message so it is displayed to the user.
// When there is an error like this the form is not displayed, so we need to wrap the error div in the gform
// wrapper div in order for the validation error style to actually be applied.
$confirmation = "<div class='gform_wrapper'><div class='validation_error'>" . nl2br($e->getMessage()) . "</div></div>";
}
return $confirmation;
}//end of member function
/**
* Get success URL for 3DSecure flow.
*
* @since 2.0
*
* @param int $form_id Form ID.
* @param int $feed_id Feed ID.
*
* @return string
*/
private function get_success_url( $form_id, $feed_id ) {
/**
* Filters 2Checkout success URL, which is the URL that users will be sent to after completing 3DSecure confirmation successfully.
*
* @since 2.0
*
* @param string $url The URL to be filtered.
* @param int $form_id The ID of the form being submitted.
* @param int $feed_id The ID of the feed being processed.
*/
return apply_filters(
'gform_amazonpayfort_success_url',
add_query_arg( 'gf_amazonpayfort_3ds_success', $this->get_success_nonce(), $this->get_page_url() ), $form_id, $feed_id
);
}//end of member function
public function redirect_url( $feed, $submission_data, $form, $entry ) {
global $wp_version;
//Don't process redirect url if request is a Borgun return
if ( ! rgempty( 'gf_borgun_return', $_GET ) ) {
return false;
}
//updating lead's payment_status to Processing
GFAPI::update_entry_property( $entry['id'], 'payment_status', 'Processing' );
//Getting Url (Production or Sandbox)
$url = $feed['meta']['mode'] == 'production' ? $this->production_url : $this->sandbox_url;
$settings = $this->get_plugin_settings();
$gf_borgun_merchant_id = ! empty( $settings['gf_borgun_merchant_id'] ) ? $settings['gf_borgun_merchant_id'] : '';
$gf_borgun_payment_gateway_id = ! empty( $settings['gf_borgun_payment_gateway_id'] ) ? $settings['gf_borgun_payment_gateway_id'] : '';
$gf_borgun_secret_key = ! empty( $settings['gf_borgun_secret_key'] ) ? $settings['gf_borgun_secret_key'] : '';
$gf_borgun_language = ! empty( $settings['gf_borgun_language'] ) ? $settings['gf_borgun_language'] : 'en';
$gf_borgun_notification_email = ! empty( $settings['gf_borgun_notification_email'] ) ? $settings['gf_borgun_notification_email'] : get_option( 'admin_email' );
if ( empty( $gf_borgun_merchant_id ) && empty( $gf_borgun_secret_key ) && empty( $gf_borgun_payment_gateway_id ) ) {
return false;
}
$line_items = rgar( $submission_data, 'line_items' );
$discounts = rgar( $submission_data, 'discounts' );
$payment_amount = rgar( $submission_data, 'payment_amount' );
$currency = rgar( $entry, 'currency' );
$return = $this->return_url( $form['id'], $entry['id'], $payment_amount );
$hash[] = $gf_borgun_merchant_id;
$hash[] = $return;
$hash[] = $return;
$hash[] = $entry['id'];
$hash[] = number_format( $payment_amount, 2, '.', '' );
//print_r( $hash );
$hash[] = $currency;
$message = implode( '|', $hash );
$CheckHashMessage = utf8_encode( trim( $message ) );
$hash = hash_hmac( 'sha256', $CheckHashMessage, $gf_borgun_secret_key );
//Customer fields
$customer_fields = $this->customer_query_string( $feed, $entry );
$borgun_args = array(
'merchantid' => $gf_borgun_merchant_id,
'paymentgatewayid' => $gf_borgun_payment_gateway_id,
'checkhash' => $hash,
'orderid' => $entry['id'],
'currency' => $currency,
'language' => $gf_borgun_language,
'SourceSystem' => 'GF-' . $wp_version . ' -BRG-' . $entry['id'],
'buyeremail' => $customer_fields['email'],
'amount' => number_format( $payment_amount, 2, '.', '' ),
'pagetype' => '0',
//If set as 1 then cardholder is required to insert email,mobile number,address.
'skipreceiptpage' => '1',
'merchantemail' => $gf_borgun_notification_email,
);
$item_loop = 0;
//work on products
if ( is_array( $line_items ) ) {
foreach ( $line_items as $item ) {
$product_name = $item['name'];
$quantity = $item['quantity'];
$price = $item['unit_price'];
$borgun_args[ 'itemdescription_' . $item_loop ] = html_entity_decode( $product_name, ENT_NOQUOTES, 'UTF-8' );
$borgun_args[ 'itemcount_' . $item_loop ] = $quantity;
$options = rgar( $item, 'options' );
$is_shipping = rgar( $item, 'is_shipping' );
if ( ! $is_shipping ) {
if ( ! empty( $options ) && is_array( $options ) ) {
$option_index = 1;
$options_string = ' (';
foreach ( $options as $option ) {
$field_label = html_entity_decode( $option["field_label"], ENT_NOQUOTES, 'UTF-8' );
$option_name = html_entity_decode( $option["option_name"], ENT_NOQUOTES, 'UTF-8' );
$price += GFCommon::to_number( $option["unit_price"] );
$amount = GFCommon::to_number( $option['unit_price'] );
$field_label = str_replace( '+', ' ', $field_label );
$option_name = str_replace( '+', ' ', $option_name );
$options_string .= $field_label . ':' . $option_name . '(' . $currency . ' ' . $amount . ') , ';
$option_index ++;
}
$options_string .= rtrim( $options_string, ', ' );
$product_name .= $options_string . ' )';
$borgun_args[ 'itemdescription_' . $item_loop ] = html_entity_decode( $product_name, ENT_NOQUOTES, 'UTF-8' );
$borgun_args[ 'itemcount_' . $item_loop ] = $quantity;
$borgun_args[ 'itemunitamount_' . $item_loop ] = $price;
$borgun_args[ 'itemamount_' . $item_loop ] = $price * $quantity;
} else {
$borgun_args[ 'itemunitamount_' . $item_loop ] = number_format( $price, 2, '.', '' );
$borgun_args[ 'itemamount_' . $item_loop ] = number_format( $price * $quantity, 2, '.', '' );
}
}
$item_loop ++;
}
//look for discounts to pass in the item_name
if ( is_array( $discounts ) ) {
foreach ( $discounts as $discount ) {
$product_name = $discount['name'];
$quantity = $discount['quantity'];
$price = $discount['price'];
$borgun_args[ 'itemdescription_' . $item_loop ] = html_entity_decode( $product_name, ENT_NOQUOTES, 'UTF-8' );
$borgun_args[ 'itemcount_' . $item_loop ] = $quantity;
$borgun_args[ 'itemunitamount_' . $item_loop ] = $price;
$borgun_args[ 'itemamount_' . $item_loop ] = $price * $quantity;
}
}
}
$url = $url . '?' . http_build_query( $borgun_args );
$url = $url . '&returnurlsuccess=' . $return . '&returnurlsuccessserver' . $return
. '&returnurlcancel=' . $return . '&returnurlerror=' . $return . '&merchantemail=' . $gf_borgun_notification_email;
$url = gf_apply_filters( 'gform_borgun_request', $form['id'], $url, $form, $entry, $feed, $submission_data );
$this->log_debug( __METHOD__ . "(): Sending to Borgun: {$url}" );
return $url;
}//end of function
public function custom_confirmation( $confirmation, $form, $entry, $ajax ) {
if( $form['id'] == '1' ) {
$confirmation = array( 'redirect' => 'http://localhost/woocommerce/gf-post.php' );
}
return $confirmation;
}//end of member function
//Get 'Current Feed'
public function getCurrentFeed() {
return $this->current_feed;
}//end of member function
//Get Authorization
public function getAuthorization() {
return $this->authorization;
}//end of member function
private function redirect_to_amazon_payfort( $entry ) {
$payment_details = gform_get_meta( $entry['id'], 'amazonpayfort_payment_details' );
$this->log_debug( '--Returned Payment Details---' );
$this->log_debug( print_r( $payment_details, true ) );
if ( ! is_array( $payment_details ) || empty( $payment_details['PaymentMethod']['Authorize3DS']['Href'] ) ) {
return;
}
gform_update_meta( $entry['id'], '3dsecure_success_nonce', wp_hash( $this->get_success_nonce() ) );
$redirect_url = add_query_arg(
rgar( $payment_details['PaymentMethod']['Authorize3DS'], 'Params', array() ),
$payment_details['PaymentMethod']['Authorize3DS']['Href']
);
$this->log_debug( '3DS Redirect URL: ' . $redirect_url );
header( 'location: ' . $redirect_url );
exit();
}//end of member function
//Creates BluePay left nav menu under Forms
public static function create_menu( $menus ){
// Adding submenu if user has access
$permission = self::has_access("gravityforms_amazon_payfort");
if(!empty($permission))
$menus[] = array( "name" => "gf_amazon_payfort", "label" => __("Amazon Payfort", "gravity-forms-amazonpayfort"),
"callback" => array("GFAmazonPayfort", "amazon_payfort_page"), "permission" => $permission );
return $menus;
}//end of member function
//——————– VALIDATION STEP ——————————————————-
public static function amazon_payfort_validation($validation_result){
print_r( $validation_result );
$config = self::is_ready_for_capture($validation_result);
$form = $validation_result["form"];
/*
if(!$config)
return $validation_result;
*/
//getting submitted data from fields
$form_data = self::get_form_data($form, $config);
$initial_payment_amount = $form_data["amount"] + absint(rgar($form_data,"fee_amount"));
//don't process payment if initial payment is 0, but act as if the transaction was successful
if($initial_payment_amount == 0){
self::log_debug("Amount is 0. No need to authorize payment, but act as if transaction was successful");
self::process_free_product($form, $config, $validation_result);
}
else {
$card_field = self::get_creditcard_field($form);
if($card_field && rgpost("input_{$card_field["id"]}_1") && rgpost("input_{$card_field["id"]}_2") && rgpost("input_{$card_field["id"]}_3") && rgpost("input_{$card_field["id"]}_5") ){
self::log_debug("Initial payment of {$initial_payment_amount}. Credit card authorization required.");
//authorizing credit card and setting self::$transaction_response variable
$validation_result = self::authorize_credit_card($form_data, $config, $validation_result);
}else{
self::log_debug("Initial payment of {$initial_payment_amount}. ACH authorization required.");
//authorizing credit card and setting self::$transaction_response variable
$validation_result = self::authorize_ach($form_data, $config, $validation_result);
}
}
return $validation_result;
}//end of member function
//-------------------- SUBMISSION STEP -------------------------------------------------------
public static function amazon_payfort_save_field_value( $value, $lead, $field, $form ){
if(empty(self::$transaction_response))
return $value;
$config = self::$transaction_response["config"];
if($field['id'] == $config['meta']['customer_fields']['routing_number']){
return '######'.substr($value, 6);
}
if($field['id'] == $config['meta']['customer_fields']['account_number']){
return '######'.substr($value, 6);
}
return $value;
}//end of member validation
//Is Delayed Pre Process Feed?
public function gform_is_delayed_pre_process_feed( $is_delayed, $form, $entry, $slug ) {
require_once 'includes/class-gf-amazonpayfort-gform-is-delayed-pre-process-feed.php';
return GFAmazonPayfort_Is_Delayed_Pre_Process_Feed::instance( $this )->gform_is_delayed_pre_process_feed( $is_delayed, $form, $entry, $slug );
}//end of member function
//GFPaymentAddOn Process Feed
public function process_feed( $feed, $entry, $form ) {
require_once 'amazon-payfort-gf/classes/class-gf-amazonpayfort-process-feed.php';
return GFAmazonPayfort_Process_Feed::instance( $this )->process_feed( $feed, $entry, $form );
}//end of member function
/**
* Retrieves an entry by looking up for the success nonce that was saved before sending the user to 3DSecure challenge.
*
* @since 2.0
*
* @param string $nonce The success token.
*
* @return false|array The entry if found, or false.
*/
//No Use of this function
/*
private function get_entry_by_3dsecure_nonce( $nonce ) {
if ( empty( $nonce ) ) {
return false;
}
$entries = GFAPI::get_entries( 0,
array(
'field_filters' => array(
array(
'key' => '3dsecure_success_nonce',
'value' => wp_hash( $nonce ),
),
),
)
);
if ( is_wp_error( $entries ) || ! is_array( $entries ) || count( $entries ) < 1 ) {
return false;
}
return $entries[0];
}//end of member function
*/
/**
* Completes processing the entry payment information after successful 3DSecure flow.
*
* @since 2.0
*
* @param array $entry Current entry object being processed.
*
* @return array The entry array after updating its payment information.
*/
//No Use of this function
/*
private function process_entry_after_successful_3dsecure( $entry ) {
// Only process entry if it is still in pending status.
// Sometimes IPN is sent and handled before the user is redirected back to the confirmation page, check status to prevent processing the entry twice.
if ( $entry[‘payment_status’] !== ‘Pending’ ) {
return $entry;
}
$order_details = gform_get_meta( $entry['id'], 'order_details' );
$order_type = gform_get_meta( $entry['id'], 'order_type' );
$form = GFAPI::get_form( $entry['form_id'] );
if ( $order_type === 'subscription' ) {
$entry = parent::process_subscription(
array(
'subscription' => array(
'subscription_id' => $order_details['RefNo'],
'is_success' => true,
'amount' => $order_details['NetPrice'],
),
),
array(),
array(),
$form,
$entry
);
} else {
parent::complete_authorization( $entry,
array(
'amount' => $order_details['NetPrice'],
'transaction_id' => $order_details['RefNo'], ) );
}
return $entry;
}//end of member function
*/
/**
* Handles displaying/processing entry confirmation after successful 3DSecure flow.
*
* @since 2.0
*
* @param array $entry Current entry object being processed.
*/
private function handle_confirmation( $entry ) {
if ( ! class_exists( 'GFFormDisplay' ) ) {
require_once( GFCommon::get_base_path() . '/form_display.php' );
}
$form = GFAPI::get_form( $entry['form_id'] );
$confirmation = GFFormDisplay::handle_confirmation( $form, $entry, false );
if ( is_array( $confirmation ) && isset( $confirmation['redirect'] ) ) {
header( "Location: {$confirmation['redirect']}" );
exit;
}
GFFormDisplay::$submission[ $entry['form_id'] ] = array(
'is_confirmation' => true,
'confirmation_message' => $confirmation,
'form' => $form,
'lead' => $entry,
);
}//end of member function
/**
* Initialize the frontend hooks.
*
* @since 1.0
* @access public
*
* @uses GF_AmazonPayfort::register_init_scripts()
* @uses GF_AmazonPayfort::populate_credit_card_last_four()
* @uses GFPaymentAddOn::init()
*
* @return void
*/
public function init() {
//add_filter( 'gform_field_content', array( $this, 'add_amazonpayfort_token' ), 10, 5 );
add_filter( 'gform_register_init_scripts', array( $this, 'register_init_scripts' ), 10, 3 );
//Commented by Me
//add_action( 'admin_notices', array( $this, 'add_upgrade_connection_notice' ) );
// Supports frontend feeds.
$this->_supports_frontend_feeds = true;
parent::init();
}//end of member function
/**
* Return the scripts which should be enqueued.
*
* @since 1.0
* @since 2.0 Use 2pay.js library instead of deprecated 2co.js
* @access public
*
* @uses GF_AmazonPayfort::frontend_script_callback()
* @uses GFAddOn::get_base_url()
* @uses GFAddOn::get_short_title()
* @uses GFAddOn::get_version()
* @uses GFCommon::get_base_url()
* @uses GFPaymentAddOn::scripts()
*
* @return array
*/
public function scripts() {
$min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min';
$scripts = array(
array(
'handle' => '2pay.js',
'src' => 'https://2pay-js.amazonpayfort.com/v1/2pay.js',
'version' => $this->get_version(),
'deps' => array(),
),
/*
array(
'handle' => 'gform_amazonpayfort_frontend',
'src' => $this->get_base_url() . "/js/frontend{$min}.js",
'version' => $this->get_version(),
'deps' => array( 'jquery', '2pay.js', 'wp-a11y' ),
'in_footer' => false,
'enqueue' => array(
array( $this, 'frontend_script_callback' ),
),
),
*/
);
return array_merge( parent::scripts(), $scripts );
}//end of member function
/***
* Return the styles that need to be enqueued.
*
* @since 2.0
*
* @return array Returns an array of styles and when to enqueue them
*/
public function styles() {
$min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min';
$styles = array(
array(
'handle' => 'gforms_amazonpayfort_frontend',
'src' => $this->get_base_url() . "/css/frontend{$min}.css",
'version' => $this->_version,
'in_footer' => false,
'enqueue' => array(
array( $this, 'frontend_script_callback' ),
),
),
array(
'handle' => 'gforms_amazonpayfort_plugin_settings',
'src' => $this->get_base_url() . "/css/plugin_settings{$min}.css",
'version' => $this->_version,
'in_footer' => false,
'enqueue' => array(
array(
'admin_page' => array( 'form_settings', 'form_editor' ),
),
),
),
);
return array_merge( parent::styles(), $styles );
}//end of member function
/**
* Check if the form has an active Amazon Payfort feed and a credit card field.
*
* @since 1.0
* @since 2.0 Check if Amazon Payfort field exists instead of default credit card field.
* @access public
*
* @used-by GF_AmazonPayfort::scripts()
* @uses GFFeedAddOn::has_feed()
* @uses GFPaymentAddOn::has_credit_card_field()
*
* @param array $form The form currently being processed.
*
* @return bool
*/
public function frontend_script_callback( $form ) {
return ! is_admin() && $form && $this->has_feed( $form['id'] ) && $this->has_amazonpayfort_card_field( $form );
}//end of member function
/**
* Add required Amazon Payfort token hidden input in case of a multi-page form.
*
* @since 2.0
*
* @param string $content The field content to be filtered.
* @param object $field The field that this input tag applies to.
* @param string $value The default/initial value that the field should be pre-populated with.
* @param integer $lead_id When executed from the entry detail screen, $lead_id will be populated with the Entry ID.
* @param integer $form_id The current Form ID.
*
* @return string $content HTML formatted content.
*/
//Not Useful
public function add_amazonpayfort_token( $content, $field, $value, $lead_id, $form_id ) {
// If this form does not have a Amazon Payfort feed or if this is not a Amazon Payfort field, return field content.
if ( ! $this->has_feed( $form_id ) || $field->get_input_type() !== 'amazonpayfort_creditcard' ) {
return $content;
}
// Populate Amazon Payfort token to hidden fields if it exists.
$token = sanitize_text_field( rgpost( 'amazonpayfort_response' ) );
if ( $token ) {
$content .= '<input type="hidden" name="amazonpayfort_response" value="' . esc_attr( $token ) . '" />';
}
return $content;
}//end of member function
// # PLUGIN SETTINGS -----------------------------------------------------------------------------------------------
/**
* Prepare plugin settings fields.
*
* @since 1.0
* @since 2.0 Use merchant code & secret and deprecate sandbox credentials.
* @access public
*
* @return array
* @uses GF_AmazonPayfort::initialize_api()
*/
public function plugin_settings_fields() {
$tooltip =
sprintf(
// Translators: 1. Open anchor tag 2. close anchor tag.
esc_html__( 'Enter your Amazon Payfort API credentials below. For more information about these settings, check out the %1$sGravity Forms documentation.%2$s', 'amazon-payfort-gf' ), '<a href="https://docs.gravityforms.com/setting-up-the-amazonpayfort-add-on/" target="_blank" title="Setting up the Amazon Payfort Add-on">', '</a>' );
$set_array = array(
array(
'title' => esc_html__( 'Amazon Payfort API Mode', 'amazon-payfort-gf' ),
'fields' => array(
array(
'name' => 'apiMode',
'label' => esc_html__( 'Amazon PayFort API Mode', 'amazon-payfort-gf' ),
'type' => 'radio',
'required' => true,
'horizontal' => true,
'default_value' => 'sandbox',
'choices' => array(
array(
'label' => esc_html__( 'Production', 'amazon-payfort-gf' ),
'value' => 'production',
),
array(
'label' => esc_html__( 'Sandbox', 'amazon-payfort-gf' ),
'value' => 'sandbox',
),
),
),
),
),
array(
'title' => esc_html__( 'Amazon Payfort API Credentials', 'amazon-payfort-gf' ),
'tooltip' => $tooltip,
'fields' => array(
array(
'name' => 'merchant_identifier',
'label' => esc_html__( 'Merchant Identifier', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
//'feedback_callback' => array( $this, 'validate_production_credentials' ),
'required' => true,
),
array(
'name' => 'access_code',
'label' => esc_html__( 'Access Code ', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
//'feedback_callback' => array( $this, 'validate_production_credentials' ),
'required' => true,
),
array(
'name' => 'sha_request_phrase',
'label' => esc_html__( 'SHA Request Phrase', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
),
array(
'name' => 'sha_response_phrase',
'label' => esc_html__( 'SHA Response Phrase', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
),
/*
array(
'name' => 'IPN_enabled',
'label' => esc_html__( 'IPN Configured?', 'amazon-payfort-gf' ),
'type' => 'checkbox',
'horizontal' => true,
'required' => true,
'description' => $this->get_ipn_section_description(),
'choices' => array(
array(
'label' => esc_html__( 'I have enabled the Gravity Forms IPN URL in my Amazon Payfort account.', 'amazon-payfort-gf' ),
'value' => 1,
'name' => 'IPN_enabled',
),
),
),
*/
),
),
);
return array(
array(
'title' => esc_html__( 'Amazon PayFort API Mode', 'amazon-payfort-gf' ),
'fields' => array(
array(
'name' => 'apiMode',
'label' => esc_html__( 'API Mode', 'amazon-payfort-gf' ),
'type' => 'radio',
'required' => true,
'horizontal' => true,
'default_value' => 'sandbox',
'choices' => array(
array(
'label' => esc_html__( 'Production', 'amazon-payfort-gf' ),
'value' => 'production',
),
array(
'label' => esc_html__( 'Sandbox', 'amazon-payfort-gf' ),
'value' => 'sandbox',
),
),
),
),
),
array(
'title' => esc_html__( 'Amazon PayFort API Credentials', 'amazon-payfort-gf' ),
'tooltip' => $tooltip,
'fields' => array(
array(
'name' => 'merchant_identifier',
'label' => esc_html__( 'Merchant Identifier', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
//'feedback_callback' => array( $this, 'validate_production_credentials' ),
'required' => true,
),
array(
'name' => 'access_code',
'label' => esc_html__( 'Access Code ', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
//'feedback_callback' => array( $this, 'validate_production_credentials' ),
'required' => true,
),
array(
'name' => 'sha_request_phrase',
'label' => esc_html__( 'SHA Request Phrase', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
),
array(
'name' => 'sha_response_phrase',
'label' => esc_html__( 'SHA Response Phrase', 'amazon-payfort-gf' ),
'type' => 'text',
'class' => 'medium',
),
/*
array(
'name' => 'IPN_enabled',
'label' => esc_html__( 'IPN Configured?', 'amazon-payfort-gf' ),
'type' => 'checkbox',
'horizontal' => true,
'required' => true,
'description' => $this->get_ipn_section_description(),
'choices' => array(
array(
'label' => esc_html__( 'I have enabled the Gravity Forms IPN URL in my Amazon Payfort account.', 'amazon-payfort-gf' ),
'value' => 1,
'name' => 'IPN_enabled',
),
),
),
*/
),
),
);
}//end of member function
/**
* Return the plugin's icon for the plugin/form settings menu.
*
* @since 1.7
*
* @return string
*/
public function get_menu_icon() {
//return file_get_contents( $this->get_base_path() . '/images/menu-icon.svg' );
return 'gform-icon--credit-card';
}//end of member function
/**
* Validate production API credentials.
*
* @since 1.0
* @access public
*
* @used-by GF_AmazonPayfort::plugin_settings_fields()
* @uses GF_AmazonPayfort::initialize_api()
*
* @return bool|null
*/
public function validate_production_credentials() {
// Capture API response.
$api_response = $this->initialize_api( 'production' );
return is_a( $api_response, 'GF_AmazonPayfort_API' ) ? true : $api_response;
}//end of member function
/**
* Validate sandbox API credentials.
*
* @since 1.0
* @deprecated 2.0 No longer used by internal code.
* @access public
*
* @used-by GF_AmazonPayfort::plugin_settings_fields()
* @uses GF_AmazonPayfort::initialize_api()
*
* @return bool|null
*/
public function validate_sandbox_credentials() {
//Capture API response.
$api_response = $this->initialize_api( 'sandbox' );
return is_a( $api_response, 'GF_AmazonPayfort_API' ) ? true : $api_response;
}//end of member function
// # FEED SETTINGS -------------------------------------------------------------------------------------------------
/**
* Set feed creation control.
*
* @since 1.0
* @since 2.0 Check if a Amazon Payfort field exists.
* @access public
*
* @uses GF_AmazonPayfort::initialize_api()
*
* @return bool
*/
public function can_create_feed() {
return is_a( $this->initialize_api(), 'GF_AmazonPayfort_API' ) && $this->has_amazonpayfort_card_field();
}//end of member function
/**
* Get the require Amazon Payfort field message.
*
* @since 2.0
*
* @return false|string
*/
public function feed_list_message() {
$form = $this->get_current_form();
// If settings are not yet configured, display default message.
if ( ! is_a( $this->initialize_api(), 'GF_AmazonPayfort_API' ) ) {
return GFFeedAddOn::feed_list_message();
}
// If form doesn't have a Amazon Payfort field, display require message.
if ( ! $this->has_amazonpayfort_card_field( $form ) ) {
return $this->requires_amazonpayfort_card_message();
}
return GFFeedAddOn::feed_list_message();
}//end of member function
/**
* Display require Amazon Payfort field message.
*
* @since 2.0
*
* @return string
*/
public function requires_amazonpayfort_card_message() {
$url = add_query_arg(
array(
'view' => null,
'subview' => null,
)
);
return sprintf( esc_html__( “You must add an Amazon Payfort field to your form before creating a feed. Let’s go %1\$sadd one%2\$s!”, ‘amazon-payfort-gf’ ), ““, ‘‘ );
}//end of member function
/**
* Prepare a list of needed billing information fields.
*
* @since 1.0
* @access public
*
* @return array
*/
public function billing_info_fields() {
$fields = array(
array(
'name' => 'email',
'label' => esc_html__( 'Email', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'address',
'label' => esc_html__( 'Address', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'address2',
'label' => esc_html__( 'Address 2', 'amazon-payfort-gf' ),
'required' => false,
),
array(
'name' => 'city',
'label' => esc_html__( 'City', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'state',
'label' => esc_html__( 'State', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'zip',
'label' => esc_html__( 'Zip', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'country',
'label' => esc_html__( 'Country', 'amazon-payfort-gf' ),
'required' => true,
),
array(
'name' => 'phone',
'label' => esc_html__( 'Phone Number', 'amazon-payfort-gf' ),
'required' => true,
),
);
return $fields;
}//end of member function
/**
* Define the choices available in the billing cycle drop downs.
*
* @since 1.0
* @access public
*
* @used-by GFPaymentAddOn::settings_billing_cycle()
*
* @return array
*/
public function supported_billing_intervals() {
return array(
'week' => array(
'label' => esc_html__( 'week(s)', 'amazon-payfort-gf' ),
'min' => 1,
'max' => 12,
),
'month' => array(
'label' => esc_html__( 'month(s)', 'amazon-payfort-gf' ),
'min' => 1,
'max' => 12,
),
'year' => array(
'label' => esc_html__( 'year(s)', 'amazon-payfort-gf' ),
'min' => 1,
'max' => 1,
),
);
}//end of member function
/**
* Define the option choices available.
*
* @since 1.0
* @access public
*
* @used-by GFPaymentAddOn::other_settings_fields()
*
* @return array
*/
public function option_choices() {
return array();
}//end of member function
// # FRONTEND ------------------------------------------------------------------------------------------------------
/**
* Register Amazon Payfort script when displaying form.
*
* @since 1.0
* @access public
*
* @param array $form Form object.
* @param array $field_values Current field values. Not used.
* @param bool $is_ajax If form is being submitted via AJAX.
*
* @used-by GF_AmazonPayfort::init()
* @uses GFAddOn::get_plugin_settings()
* @uses GFFeedAddOn::has_feed()
* @uses GFFormDisplay::add_init_script()
* @uses GFFormDisplay::ON_PAGE_RENDER
* @uses GFPaymentAddOn::get_credit_card_field()
*/
public function register_init_scripts( $form, $field_values, $is_ajax ) {
// Get Amazon Payfort field.
$cc_field = $this->get_amazonpayfort_card_field( $form );
// If form does not have a Amazon Payfort feed and does not have a credit card field, exit.
if ( ! $this->has_feed( $form['id'] ) || ! $cc_field || ! $this->initialize_api() ) {
return;
}
// Get plugin settings.
$settings = $this->get_plugin_settings();
// Prepare Amazon Payfort Javascript arguments.
$args = array(
'apiMode' => $settings['apiMode'],
'formId' => $form['id'],
'ccFieldId' => $cc_field->id,
'ccPage' => $cc_field->pageNumber,
'isAjax' => $is_ajax,
'merchantIdentifier' => $settings['merchant_identifier'],
'accessCode' => $settings['access_code'],
'shaRequestPhrase' => $settings['sha_request_phrase'],
'shaResponsePhrase' => $settings['sha_response_phrase'],
);
// get all Amazon Payfort feeds.
$feeds = $this->get_feeds_by_slug( $this->_slug, $form['id'] );
foreach ( $feeds as $feed ) {
if ( rgar( $feed, 'is_active' ) === '0' ) {
continue;
}
// Get feed settings to pass them to JS object.
$feed_settings = array(
'feedId' => $feed['id'],
);
if ( rgars( $feed, 'meta/transactionType' ) === 'product' ) {
$feed_settings['paymentAmount'] = rgars( $feed, 'meta/paymentAmount' );
}
$args['feeds'][ $feed['id'] ] = $feed_settings;
}//end of foreach loop
// Initialize Amazon Payfort script.
$script = 'new GFAmazon Payfort( ' . json_encode( $args ) . ' );';
// Add Amazon Payfort script to form scripts.
GFFormDisplay::add_init_script( $form['id'], 'amazonpayfort', GFFormDisplay::ON_PAGE_RENDER, $script );
}//end of member function
/**
* Gets the payment validation result.
*
* @since 2.0
*
* @param array $validation_result Contains the form validation results.
* @param array $authorization_result Contains the form authorization results.
*
* @return array The validation result for the credit card field.
*/
public function get_validation_result( $validation_result, $authorization_result ) {
if ( empty( $authorization_result['error_message'] ) ) {
return $validation_result;
}
$credit_card_page = 0;
foreach ( $validation_result['form']['fields'] as &$field ) {
if ( $field->type === 'amazonpayfort_creditcard' ) {
$field->failed_validation = true;
$field->validation_message = $authorization_result['error_message'];
$credit_card_page = $field->pageNumber;
break;
}
}//end of foreach loop
$validation_result['credit_card_page'] = $credit_card_page;
$validation_result['is_valid'] = false;
return $validation_result;
}//end of member function
// # TRANSACTIONS --------------------------------------------------------------------------------------------------
/**
* Initialize authorizing the transaction for the product & services type feed or return the 2co.js error.
*
* @since 1.0
* @access public
*
* @param array $feed The Feed object currently being processed.
* @param array $submission_data The customer and transaction data.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
*
* @uses GF_AmazonPayfort::authorize_product()
* @uses GF_AmazonPayfort::get_amazonpayfort_js_error()
* @uses GF_AmazonPayfort::initialize_api()
* @uses GFPaymentAddOn::authorization_error()
*
* @return array
*/
public function authorize( $feed, $submission_data, $form, $entry ) {
// Initialize API.
if ( ! is_a( $this->initialize_api(), 'GF_AmazonPayfort_API' ) ) {
return $this->authorization_error( esc_html__( 'Unable to initialize API.', 'amazon-payfort-gf' ) );
}
$validation_errors = $this->validate_submission( $submission_data, $form, $entry, $feed );
// If there were validation errors, return them.
if ( is_array( $validation_errors ) ) {
return $validation_errors;
}
// Authorize product.
return $this->authorize_product( $feed, $submission_data, $form, $entry );
}//end of member function
/**
* Create the Amazon Payfort sale authorization and return any authorization errors which occur.
*
* @since 1.0
* @since 2.0 Use API version 6.
* @access public
*
* @param array $feed The Feed object currently being processed.
* @param array $submission_data The customer and transaction data.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
*
* @used-by GF_AmazonPayfort::authorize()
* @uses GFAddOn::get_field_map_fields()
* @uses GFAddOn::get_field_value()
* @uses GFAddOn::log_debug()
* @uses GFAddOn::log_error()
* @uses GF_AmazonPayfort::prepare_sale()
* @uses GF_AmazonPayfort::validate_customer_info()
* @uses GF_AmazonPayfort_API::create_sale()
* @uses GFPaymentAddOn::authorization_error()
*
* @return array
*/
public function authorize_product( $feed, $submission_data, $form, $entry ) {
// Create order object.
$order_object = $this->create_order_object( $submission_data, $form, $entry, $feed );
$this->log_debug( __METHOD__ . '(): Order to be created; ' . print_r( $order_object, true ) );
// Create & validate order.
$order = $this->api->create_order( $order_object );
$order_validation_errors = $this->validate_order_details( $order );
if ( is_array( $order_validation_errors ) ) {
return $order_validation_errors;
}
$this->log_debug( __METHOD__ . '(): Order created Successfully; ' . print_r( $order, true ) );
// Prepare authorization response.
return array(
'is_authorized' => true,
'transaction_id' => $order['RefNo'],
'payment_details' => $order['PaymentDetails'],
);
}//end of member function
/**
* Capture the Amazon Payfort charge which was authorized during validation.
*
* @since 1.0
* @since 2.0 Use API version 6.
* @access public
*
* @param array $auth Contains the result of the authorize() function.
* @param array $feed The Feed object currently being processed.
* @param array $submission_data The customer and transaction data.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
*
* @uses GF_AmazonPayfort::initialize_api()
* @uses GF_AmazonPayfort_API::detail_sale()
* @uses GFPaymentAddOn::authorization_error()
*
* @return array
*/
public function capture( $auth, $feed, $submission_data, $form, $entry ) {
// Initialize API.
if ( ! is_a( $this->initialize_api(), 'GF_AmazonPayfort_API' ) ) {
return $this->authorization_error( esc_html__( 'Unable to initialize API.', 'amazon-payfort-gf' ) );
}
// Validate order was successfully authorized.
if ( ! $auth['is_authorized'] || empty( $auth['transaction_id'] ) ) {
return array(
'is_success' => false,
'error_message' => $auth['error_message'],
);
}
// Get order details.
$order_details = $this->api->get_order( $auth['transaction_id'] );
if ( is_wp_error( $order_details ) ) {
$this->log_error( __METHOD__ . '(): Could not retrieve order; ' . $order_details->get_error_message() . ' (' . $order_details->get_error_code() . ')' );
return array(
'is_success' => false,
'error_message' => $order_details->get_error_message(),
'orderDetails' => array(),
);
}
// Store order details for later use.
$this->log_debug( 'Retrieved order: ' . print_r( $order_details, true ) );
gform_update_meta( $entry['id'], 'amazonpayfort_payment_details', $auth['payment_details'] );
gform_update_meta( $entry['id'], 'order_details', $order_details );
gform_update_meta( $entry['id'], 'order_type', 'product' );
// If order is completed already, return payment, otherwise return empty array to complete authorization.
if ( $order_details['Status'] === 'AUTHRECEIVED' || $order_details['Status'] === 'PENDING' ) {
return array();
} elseif ( $order_details['Status'] === 'COMPLETE' ) {
return array(
'is_success' => true,
'transaction_id' => $auth['transaction_id'],
'amount' => $order_details['NetPrice'],
'payment_method' => $order_details['PaymentDetails']['Type'],
'orderDetails' => $order_details,
);
}//end of If Else block
// If status is not pending or completed, then order is cancelled.
return array(
'is_success' => false,
'error_message' => __( 'Order was cancelled', 'amazon-payfort-gf' ),
);
}//end of member function
/**
* Complete authorization (mark entry as pending and create note) for the pending orders.
*
* @since 2.0
*
* @param array $entry Entry data.
* @param array $action Authorization data.
*
* @return bool
*/
public function complete_authorization( &$entry, $action ) {
$order_details = gform_get_meta( $entry['id'], 'order_details' );
if ( empty( $order_details ) ) {
return false;
}
$this->update_entry_credit_card_details( $entry, $order_details );
$this->set_pending_payment_status( $entry, $order_details );
$this->maybe_redirect_to_3dsecure( $entry );
$action['amount'] = $order_details['NetPrice'];
$action['transaction_id'] = $order_details['RefNo'];
return parent::complete_authorization( $entry, $action );
}//end of member function
/**
* Subscribe the user to a Amazon Payfort recurring sale.
*
* @since 1.0
* @since 2.0 Use API version 6.
*
* @param array $feed The Feed object currently being processed.
* @param array $submission_data The customer and transaction data.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
*
* @return array
*/
public function subscribe( $feed, $submission_data, $form, $entry ) {
// Initialize API.
if ( ! is_a( $this->initialize_api(), 'GF_AmazonPayfort_API' ) ) {
return $this->authorization_error( esc_html__( 'Unable to initialize API.', 'amazon-payfort-gf' ) );
}
// If there were validation errors, return them.
$validation_errors = $this->validate_submission( $submission_data, $form, $entry, $feed );
if ( is_array( $validation_errors ) ) {
return $validation_errors;
}
// Create order object.
$order_object = $this->create_order_object( $submission_data, $form, $entry, $feed );
$this->log_debug( __METHOD__ . '(): Order to be created; ' . print_r( $order_object, true ) );
// Create & validate order.
$order = $this->api->create_order( $order_object );
$order_validation_errors = $this->validate_order_details( $order );
if ( is_array( $order_validation_errors ) ) {
return $order_validation_errors;
}
$this->log_debug( __METHOD__ . '(): Subscription Order created; ' . print_r( $order, true ) );
// Prepare authorization response.
return array(
'is_success' => true,
'subscription_id' => $order['RefNo'],
'amount' => $submission_data['payment_amount'],
'payment_method' => $order['PaymentDetails']['Type'],
'payment_details' => $order['PaymentDetails'],
'order_details' => $order,
);
}//end of member function
/**
* Updates entry values for Amazon Payfort field with CC details returned from API.
*
* @since 2.0
*
* @param array $entry Current entry object being processed.
* @param array $order_details Order details returned from Amazon Payfort API.
*
* @return void
*/
private function update_entry_credit_card_details( &$entry, $order_details ) {
$form = GFAPI::get_form( $entry['form_id'] );
$field = $this->get_amazonpayfort_card_field( $form );
// Update entry with credit card data.
$entry[ $field['id'] . '.1' ] = empty( $order_details['PaymentDetails']['PaymentMethod']['LastDigits'] ) ? '' : 'XXXX XXXXX XXXXX ' . $order_details['PaymentDetails']['PaymentMethod']['LastDigits'];
$entry[ $field['id'] . '.4' ] = empty( $order_details['PaymentDetails']['PaymentMethod']['CardType'] ) ? '' : $order_details['PaymentDetails']['PaymentMethod']['CardType'];
GFAPI::update_entry( $entry );
}//end of member function
/**
* Check if the returned order array contains a request to start the 3DSecure flow, redirect the user to the url if so.
*
* @since 2.0
*
* @param array $entry Current entry being processed.
*/
private function maybe_redirect_to_3dsecure( $entry ) {
$payment_details = gform_get_meta( $entry['id'], 'amazonpayfort_payment_details' );
$this->log_debug( '--Returned Payment Details---' );
$this->log_debug( print_r( $payment_details, true ) );
if ( ! is_array( $payment_details ) || empty( $payment_details['PaymentMethod']['Authorize3DS']['Href'] ) ) {
return;
}
gform_update_meta( $entry['id'], '3dsecure_success_nonce', wp_hash( $this->get_success_nonce() ) );
$redirect_url = add_query_arg(
rgar( $payment_details['PaymentMethod']['Authorize3DS'], 'Params', array() ),
$payment_details['PaymentMethod']['Authorize3DS']['Href']
);
$this->log_debug( '3DS Redirect URL: ' . $redirect_url );
header( 'location: ' . $redirect_url );
exit();
}//end of member function
/**
* Sets the entry payment status as pending.
*
* @since 2.0
*
* @param array $entry Current entry object being processed.
* @param array $order_detail Order details array returned from Amazon Payfort API.
*/
private function set_pending_payment_status( $entry, $order_detail ) {
GFAPI::update_entry_property( $entry['id'], 'payment_status', 'Pending' );
GFAPI::update_entry_property( $entry['id'], 'payment_method', 'Amazon Payfort' );
GFAPI::update_entry_property( $entry['id'], 'transaction_id', $order_detail['RefNo'] );
}//end of member function
// # FORM SETTINGS -------------------------------------------------------------------------------------------------
/**
* Add supported notification events.
*
* @since 2.0
*
* @param array $form The form currently being processed.
*
* @return array|false The supported notification events. False if feed cannot be found within $form.
*/
public function supported_notification_events( $form ) {
// If this form does not have a Amazon Payfort feed, return false.
if ( ! $this->has_feed( $form['id'] ) ) {
return false;
}
// Return Amazon Payfort notification events.
return array(
'complete_payment' => esc_html__( 'Payment Completed', 'amazon-payfort-gf' ),
'refund_payment' => esc_html__( 'Payment Refunded', 'amazon-payfort-gf' ),
'fail_payment' => esc_html__( 'Payment Failed', 'amazon-payfort-gf' ),
'create_subscription' => esc_html__( 'Subscription Created', 'amazon-payfort-gf' ),
'add_subscription_payment' => esc_html__( 'Subscription Payment Added', 'amazon-payfort-gf' ),
'fail_subscription_payment' => esc_html__( 'Subscription Payment Failed', 'amazon-payfort-gf' ),
);
}//end of member function
// # HELPER METHODS ------------------------------------------------------------------------------------------------
/**
* Initializes Amazon Payfort API if credentials are valid.
*
* @since 1.0
* @since 2.0 Validate credentials by trying to generate a session id.
* @since 2.0 api_mode is always set to production as it does not affect initializing the API.
* @access public
*
* @param string $api_mode Amazon Payfort API mode (production or sandbox).
*
* @uses GFAddOn::get_current_settings()
* @uses GFAddOn::get_plugin_settings()
* @uses GFAddOn::get_slug()
* @uses GFAddOn::is_plugin_settings()
* @uses GFAddOn::log_debug()
* @uses GFAddOn::log_error()
* @uses GF_AmazonPayfort_API::detail_company_info()
*
* @return GF_AmazonPayfort_API|false An API wrapper instance or false.
*/
public function initialize_api( $api_mode = '' ) {
// Get the plugin settings.
$settings = $this->is_plugin_settings( $this->get_slug() ) ? $this->get_current_settings() : $this->get_plugin_settings();
// If API is already initialized, return.
if ( ! is_null( $this->api ) ) {
return $this->api;
}
// Load the API library.
if ( ! class_exists( 'GF_AmazonPayfort_API' ) ) {
require_once( 'includes/class-gf-amazonpayfort-api.php' );
}
// If API credentials are empty, return.
if ( !rgars($settings, 'merchant_identifier') || !rgars($settings, 'access_code') || !rgars($settings, 'sha_request_phrase')
|| !rgars($settings, 'sha_response_phrase') ) {
return false;
}//end of If block
// Log validation step.
$this->log_debug( __METHOD__ . '(): Validating API Info.' );
// Initialize a new Amazon Payfort API object.
//$twocheckout = new GF_AmazonPayfort_API( $api_mode, $settings['merchant_code'], $settings['secret_key'] );
$amazonpayfort = new GF_AmazonPayfort_API( $api_mode, $settings[‘merchant_identifier’], $settings[‘access_code’], $settings[‘sha_request_phrase’], $settings[‘sha_response_phrase’] );
if ( ! empty( $amazonpayfort->generate_session_id() ) ) {
// Log that authentication test passed.
$this->log_debug( __METHOD__ . '(): API credentials are valid.' );
// Assign API instance to class.
$this->api = $amazonpayfort;
// Remove upgrade notice now that the settings have been set.
if ( get_option( 'gf_amazonpayfort_upgrade_notice' ) ) {
update_option( 'gf_amazonpayfort_upgrade_notice', false );
}
// Update the reauthentication version.
$settings['reauth_version'] = self::LAST_REAUTHENTICATION_VERSION;
$this->update_plugin_settings( $settings );
return $this->api;
}//end of If block
// Log that authentication test failed.
$this->log_error( __METHOD__ . '(): API credentials are invalid' );
return false;
}//end of member function
/**
* Response from 2co.js is posted to the server as 'amazonpayfort_response'.
*
* @since 1.0
* @access public
*
* @used-by GF_AmazonPayfort::add_amazonpayfort_inputs()
* @uses GFAddOn::maybe_decode_json()
*
* @return array|null
*/
public function get_amazonpayfort_js_response() {
// Get response.
$response = rgpost( 'amazonpayfort_response' );
return $this->maybe_decode_json( $response );
}//end of member function
/**
* Validate customer information.
*
* @since 1.0
* @access public
*
* @param array $sale The Sale object currently being processed.
* @param array $form The Form object currently being processed.
* @param array $field_map Billing Information field map.
*
* @uses GF_AmazonPayfort::validate_billing_address()
* @uses GFFormsModel::get_field()
* @uses GFPaymentAddOn::authorization_error()
*
* @return array|bool
*/
public function validate_customer_info( $sale = array(), $form = array(), $field_map = array() ) {
// Validate name.
if ( ! rgars( $sale, 'billingAddr/name' ) ) {
return $this->authorization_error( esc_html__( "You must provide the cardholder's name.", 'amazon-payfort-gf' ) );
}
// Validate email address.
if ( ! rgars( $sale, 'billingAddr/email' ) ) {
return $this->authorization_error( esc_html__( 'You must provide your email address.', 'amazon-payfort-gf' ) );
}
// Validate phone number.
if ( ! rgars( $sale, 'billingAddr/phoneNumber' ) ) {
return $this->authorization_error( esc_html__( 'You must provide your phone number.', 'amazon-payfort-gf' ) );
}
// Validate billing address.
if ( ! $this->validate_billing_address( $sale['billingAddr'], GFFormsModel::get_field( $form, $field_map['country'] ) ) ) {
return $this->authorization_error( esc_html__( 'You must provide a valid billing address.', 'amazon-payfort-gf' ) );
}
return true;
}//end of member function
/**
* Checks if current form has a amazonpayfort field.
*
* @since 2.0
*
* @param array $form The form currently being processed.
*
* @return bool
*/
public function has_amazonpayfort_card_field( $form = null ) {
return false !== $this->get_amazonpayfort_card_field( $form );
}//end of member function
/**
* Retrieves amazonpayfort field from current form.
*
* @since 2.0
*
* @param array $form The form currently being processed.
*
* @return bool|GF_Field_AmazonPayfort_CreditCard he Amazon Payfort field object, if found. Otherwise, false.
*/
public function get_amazonpayfort_card_field( $form = null ) {
if ( is_null( $form ) ) {
$form = $this->get_current_form();
}
$fields = GFAPI::get_fields_by_type( $form, array( 'amazonpayfort_creditcard' ) );
return empty( $fields ) ? false : $fields[0];
}//end of member function
/**
* Validates submitted data contains required values to create a Amazon Payfort sale.
*
* @since 2.0
*
* @param array $submission_data The submitted data.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
* @param array $feed The Feed object currently being processed.
*
* @return bool|array Authorization error array or true if data is valid.
*/
public function validate_submission( $submission_data, $form, $entry, $feed ) {
// Validate name.
$ccField = $this->get_amazonpayfort_card_field( $form );
$name = empty( $entry[ $ccField->id . '.5' ] ) ? array() : explode( ' ', $entry[ $ccField->id . '.5' ] );
if ( count( $name ) < 2 ) {
return $this->authorization_error( esc_html__( "You must provide the cardholder's full name.", 'amazon-payfort-gf' ) );
}
// Get field mapping information and validate submission data.
$field_map = $this->get_field_map_fields( $feed, 'billingInformation' );
// Validate Address.
if ( empty( $this->get_field_value( $form, $entry, $field_map['address'] ) )
|| empty( $this->get_field_value( $form, $entry, $field_map['city'] ) )
|| empty( $this->get_field_value( $form, $entry, $field_map['state'] ) )
|| empty( $this->get_field_value( $form, $entry, $field_map['zip'] ) )
|| empty( $this->get_field_value( $form, $entry, $field_map['country'] ) )
) {
return $this->authorization_error( esc_html__( 'You must provide a valid billing address.', 'amazon-payfort-gf' ) );
}
// Validate email address.
if ( empty( $this->get_field_value( $form, $entry, $field_map['email'] ) ) ) {
return $this->authorization_error( esc_html__( 'You must provide your email address.', 'amazon-payfort-gf' ) );
}
// Validate phone number.
if ( empty( $this->get_field_value( $form, $entry, $field_map['phone'] ) ) ) {
return $this->authorization_error( esc_html__( 'You must provide your phone number.', 'amazon-payfort-gf' ) );
}
return true;
}//end of member function
/**
* Creates a Amazon Payfort Order object.
*
* @since 2.0
*
* @param array $submission_data The customer and transaction data.
* or recurring billing information if this is a subscription order.
* @param array $form The Form object currently being processed.
* @param array $entry The Entry object currently being processed.
* @param array $feed The Feed object currently being processed.
*
* @return Object Created order object.
*/
public function create_order_object( $submission_data, $form, $entry, $feed ) {
// Create order object.
$order = new stdClass();
$order->Currency = GFCommon::get_currency();
$order->LocalTime = gmdate( 'Y-m-d H:i:s' );
$order->Items = array();
// Add line items.
if ( $feed['meta']['transactionType'] != 'subscription' ) {
$order = $this->prepare_single_payment( $order, $submission_data, $feed );
} else {
$order = $this->prepare_subscription( $order, $submission_data, $feed );
}
// Add Name and billing information.
$order = $this->prepare_billing( $order, $submission_data, $entry, $feed, $form );
// Add payment details.
$order = $this->prepare_payment_details( $order, $feed );
return $order;
}//end of member function
/**
* Validates order object was created successfully.
*
* @since 2.0
*
* @param object|WP_Error $order Order object returned from API or WP_Error.
*
* @return array|bool Authorization error array or true.
*/
public function validate_order_details( $order ) {
if ( is_wp_error( $order ) ) {
$this->log_error( __METHOD__ . '(): Could not create order; ' . $order->get_error_message() . ' (' . $order->get_error_code() . ')' );
return $this->authorization_error( $order->get_error_message() );
} elseif ( ! empty( $order['Errors'] ) && is_array( $order['Errors'] ) ) {
$error = array_pop( $order['Errors'] );
$this->log_error( __METHOD__ . '(): Order created with an error; ' . $error );
return $this->authorization_error( $error );
}
return true;
}//end of member function
/**
* Adds line items and discounts to single payment order object.
*
* @since 2.0
*
* @param object $order Amazon Payfort order object.
* @param array $submission_data Submitted form data.
* @param array $feed Current feed object being processed.
*
* @return object Amazon Payfort Order object.
*/
private function prepare_single_payment( $order, $submission_data, $feed ) {
// Add line items.
foreach ( $submission_data['line_items'] as $line_item ) {
$item = new stdClass();
$item->Code = null;
$item->Quantity = $line_item['quantity'];
$item->PurchaseType = 'PRODUCT';
$item->Tangible = false;
$item->IsDynamic = true;
$item->Price = new stdClass();
$item->Price->Amount = $line_item['unit_price'];
$item->Price->Type = 'CUSTOM';
$item->Name = $line_item['name'];
$item->Description = $line_item['description'];
$order->Items[] = $item;
}//end of foreach loop
// Add discounts.
if ( is_array( $submission_data['discounts'] ) ) {
foreach ( $submission_data['discounts'] as $discount ) {
$discount_item = new stdClass();
$discount_item->Name = $discount['name'];
$discount_item->PurchaseType = 'COUPON';
$discount_item->IsDynamic = true;
$discount_item->Quantity = (int) $discount['quantity'];
$discount_item->Price = new stdClass();
$discount_item->Price->Amount = (float) ( $discount['unit_price'] * -1 );
$order->Items[] = $discount_item;
}//end of foreach loop
}//end of If block
return $order;
}//end of member function
/**
* Adds billing and delivery information to order object.
*
* @since 2.0
*
* @param object $order Amazon Payfort order object.
* @param array $submission_data Submitted form data.
* @param array $entry The Entry object currently being processed.
* @param array $feed Current feed object being processed.
* @param array $form Current form object being processed.
*
* @return object Amazon Payfort Order object.
*/
private function prepare_billing( $order, $submission_data, $entry, $feed, $form ) {
// Get field mapping information and validate submission data.
$field_map = $this->get_field_map_fields( $feed, 'billingInformation' );
$ccField = $this->get_amazonpayfort_card_field( $form );
$name = explode( ' ', $entry[ $ccField->id . '.5' ] );
// Add billing information.
$order->BillingDetails = new stdClass();
$order->BillingDetails->Address1 = $this->get_field_value( $form, $entry, $field_map['address'] );
$order->BillingDetails->Address2 = $this->get_field_value( $form, $entry, $field_map['address2'] );
$order->BillingDetails->City = $this->get_field_value( $form, $entry, $field_map['city'] );
$order->BillingDetails->State = $this->get_field_value( $form, $entry, $field_map['state'] );
$order->BillingDetails->CountryCode = GFCommon::get_country_code( $this->get_field_value( $form, $entry, $field_map['country'] ) );
$order->BillingDetails->Phone = $this->get_field_value( $form, $entry, $field_map['phone'] );
$order->BillingDetails->Email = $this->get_field_value( $form, $entry, $field_map['email'] );
$order->BillingDetails->FirstName = $name[0];
$order->BillingDetails->LastName = $name[1];
$order->BillingDetails->Zip = $this->get_field_value( $form, $entry, $field_map['zip'] );
// Add delivery information ( same as billing ).
$order->DeliveryDetails = $order->BillingDetails;
return $order;
}
/**
* Adds payment information to order object.
*
* @since 2.0
*
* @param object $order Amazon Payfort order object.
* @param array $feed Current feed object being processed.
*
* @return object Amazon Payfort Order object.
*/
private function prepare_payment_details( $order, $feed ) {
$settings = $this->get_plugin_settings();
$api_mode = $settings['apiMode'];
$form = $this->get_current_form();
// Add payment details token.
$order->PaymentDetails = new stdClass();
$order->PaymentDetails->Type = $api_mode === 'sandbox' ? 'TEST' : 'EES_TOKEN_PAYMENT';
$order->PaymentDetails->Currency = GFCommon::get_currency();
$order->PaymentDetails->PaymentMethod = new stdClass();
$order->PaymentDetails->PaymentMethod->EesToken = $this->get_amazonpayfort_js_response();
$order->PaymentDetails->PaymentMethod->Vendor3DSReturnURL = $this->get_success_url( rgar( $form, 'id' ), rgar( $feed, 'id' ) );
$order->PaymentDetails->PaymentMethod->Vendor3DSCancelURL = $this->get_cancel_url( rgar( $form, 'id' ), rgar( $feed, 'id' ) );
if ( $feed['meta']['transactionType'] === 'subscription' ) {
$order->PaymentDetails->PaymentMethod->RecurringEnabled = true;
}
return $order;
}//end of member function
/**
* Define the markup to be displayed for the IPN section description.
*
* @since 2.0
*
* @return string HTML formatted IPN description.
*/
public function get_ipn_section_description() {
ob_start();
?>
<p>
<a href="javascript:void(0);" onclick="tb_show('IPN Instructions', '#TB_inline?width=500&inlineId=ipn-instructions', '');" onkeypress="tb_show('IPN Instructions', '#TB_inline?width=500&inlineId=ipn-instructions', '');">
<?php esc_html_e( 'View Instructions', 'amazon-payfort-gf' ); ?>
</a>
</p>
<div id="ipn-instructions" style="display:none;">
<ol class="ipn-instructions">
<li>
<?php esc_html_e( 'Click the following link and log in to access your Amazon Payfort IPN management page:', 'amazon-payfort-gf' ); ?>
<br/>
<a href="https://secure.avangate.com/cpanel/index.php" target="_blank">https://secure.avangate.com/cpanel/index.php</a>
</li>
<li><?php esc_html_e( 'Navigate to Dashboard → Integrations → Webhooks and API.', 'amazon-payfort-gf' ); ?></li>
<li><?php esc_html_e( 'Click on the IPN Settings tab.', 'amazon-payfort-gf' ); ?></li>
<li>
<?php esc_html_e( 'Click on the Add IPN URL button and add the following IPN URL.', 'amazon-payfort-gf' ); ?>
<?php echo esc_url( home_url( '/', 'https' ) . '?callback=' . $this->_slug ); ?>
</li>
<li><?php esc_html_e( 'Click Add IPN button.', 'amazon-payfort-gf' ); ?></li>
<li><?php esc_html_e( 'Scroll down to the triggers section and make sure Completed orders, Cancelled orders and Reversed and refund orders are checked.', 'amazon-payfort-gf' ); ?></li>
<li><?php esc_html_e( 'Scroll down to Response tags and click select all.', 'amazon-payfort-gf' ); ?></li>
<li><?php esc_html_e( 'Update the settings.', 'amazon-payfort-gf' ); ?></li>
</ol>
</div>
<?php
return ob_get_clean();
}//end of member function
/**
* Get cancel URL for Amazon Payfort 3DSecure flow.
*
* @since 2.0
*
* @param int $form_id Form ID.
*
* @return string
*/
private function get_cancel_url( $form_id ) {
/**
* Filters Amazon Payfort cancel URL, which is the URL that users will be sent to after cancelling/failing 3DSecure confirmation.
*
* @since 2.0
*
* @param string $url The URL to be filtered.
* @param int $form_id The ID of the form being submitted.
*/
return apply_filters( 'gform_amazonpayfort_cancel_url', $this->get_page_url(), $form_id );
}//end of function
/**
* Build the URL of the current page.
*
* @since 2.0
*
* @return string
*/
private function get_page_url() {
$page_url = GFCommon::is_ssl() ? 'https://' : 'http://';
/**
* Set the Amazon Payfort URL port if it's not 80.
*
* @since 2.0
*
* @param string Default server port.
*/
$server_port = apply_filters( 'gform_amazonpayfort_url_port', $_SERVER['SERVER_PORT'] );
if ( $server_port != '80' && $server_port != '443' ) {
$page_url .= $_SERVER['SERVER_NAME'] . ':' . $server_port . $_SERVER['REQUEST_URI'];
} else {
$page_url .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
}
return $page_url;
}//end of function
/**
* Generates a cryptographic token that is used later to verify the redirected user after successfully passing 3DS challenge.
*
* @since 2.0
*
* @return string
*/
private function get_success_nonce() {
if ( ! $this->success_nonce ) {
$this->success_nonce = wp_generate_password( 12, false );
}
return $this->success_nonce;
}//end of member function
/*
public function upgrade( $previous_version ) {
if ( version_compare( $previous_version, ‘2.0-beta-1’, ‘<‘ ) && ! $this->initialize_api() ) {
update_option( ‘gf_amazonpayfort_upgrade_notice’, true );
}
}//end of member function
*/
// # IPN Notifications ——————————————————————————————–
/**
* If the Amazon Payfort IPN belongs to a valid entry process the raw response into a standard Gravity Forms $action.
*
* @since 2.0
*
* @return array|bool Return a valid GF $action or bool if the webhook can't be processed or no action needs to be done.
*/
public function callback() {
if ( ! $this->is_gravityforms_supported() ) {
return false;
}
// Log all request data.
$this->log_debug( '--- Start logging IPN request data --- ' );
$this->log_debug( print_r( $_REQUEST, true ) );
// Maybe there is a difference.
$this->log_debug( '--- Start logging input stream data --- ' );
$request_body = trim( file_get_contents( 'php://input' ) );
$this->log_debug( print_r( $request_body, true ) );
// Hash will be unset later so cache it here.
$hash = sanitize_text_field( $_POST['HASH'] );
$order_reference_number = sanitize_text_field( $_POST['REFNO'] );
$ipn_date = sanitize_text_field( $_POST['IPN_DATE'] );
$order_total = sanitize_text_field( $_POST['IPN_TOTALGENERAL'] );
$order_status = sanitize_text_field( $_POST['ORDERSTATUS'] );
if ( ! $this->verify_ipn() ) {
$this->log_error( __METHOD__ . '(): Invalid IPN HASH, so it was not created by Gravity Forms. Aborting.' );
return false;
}
$entry_id = $this->get_entry_by_transaction_id( $order_reference_number );
if ( ! $entry_id ) {
$this->log_error( __METHOD__ . '(): Could not find entry. Aborting.' );
return false;
}
$this->output_ipn_read_receipt( $_POST );
$entry = GFAPI::get_entry( $entry_id );
$action = array(
'id' => $hash,
'entry_id' => $entry_id,
'transaction_id' => $order_reference_number,
'amount' => $order_total,
);
// Prevent already processed entries from being processed again.
if ( gform_get_meta( $entry_id, 'IPN_' . $order_status . '_PROCESSED' ) === '1' ) {
$this->log_debug( 'IPN ALREADY PROCESSED, ABORTING' );
return false;
} else {
$this->log_debug( __METHOD__ . '(): IPN request received. Starting to process => ' . print_r( $_POST, true ) );
}
// Mark entry as processed for this particular event.
gform_update_meta( $entry_id, 'IPN_' . $order_status . '_PROCESSED', '1' );
// If this is a subscription event.
$order_type = gform_get_meta( $entry_id, 'order_type' );
if ( $order_type === 'subscription' ) {
$action['subscription_id'] = $order_reference_number;
return $this->process_subscription_ipn( $entry, $action );
}
return $this->process_product_ipn( $entry, $action );
}//end of member function
/**
* Verifies that the IPN notification is a valid IPN request generated from Amazon Payfort.
*
* @since 2.0
*
* @return bool
*/
private function verify_ipn() {
if ( ! isset( $_POST['HASH'] ) ) {
$this->log_debug( 'no hash found in post' );
return false;
}
$hash = sanitize_text_field( $_POST['HASH'] );
unset( $_POST['HASH'] );
$string = '';
foreach ( $_POST as $key => $value ) {
$string .= $this->expand_array( (array) $value );
}
$this->log_debug( 'Hash string:' . $string );
$signature = hash_hmac( 'md5', $string, $this->get_plugin_setting( 'secret_key' ) );
$this->log_debug( 'Signature:' . $signature );
return $signature === $hash;
}//end of member function
/**
* Converts IPN request array into a string formatted to generate the IPN signature.
*
* @since 2.0
*
* @param array $array An array to be converted into a string.
*
* @return string
*/
private function expand_array( $array ) {
$retval = '';
foreach ( $array as $i => $value ) {
if ( is_array( $value ) ) {
$retval .= $this->expand_array( $value );
} else {
$size = strlen( $value );
$retval .= $size . $value;
}
}//end of foreach loop
return $retval;
}//end member function
/**
* Output IPN read receipt string.
*
* @since 2.0
*
* @param array $ipn_request The IPN request body.
*/
private function output_ipn_read_receipt( $ipn_request ) {
$base_string = $this->expand_array(
array(
$ipn_request['IPN_PID'][0],
$ipn_request['IPN_PNAME'][0],
$ipn_request['IPN_DATE'],
$ipn_request['IPN_DATE'],
)
);
$this->log_debug( 'Read receipt base string: ' . $base_string );
$hash = hash_hmac( 'md5', $base_string, $this->get_plugin_setting( 'secret_key' ) );
echo '<EPAYMENT>' . $ipn_request['IPN_DATE'] . '|' . $hash . '</EPAYMENT>' . PHP_EOL;
}//end of member function
// # Deprecated METHODS ——————————————————————————————–
public function populate_credit_card_last_four( $form ) {
if ( ! $this->is_payment_gateway ) {
return;
}
// If response was an error, exit.
if ( $this->get_amazonpayfort_js_error() ) {
return;
}
// Get the credit card field.
$cc_field = $this->get_credit_card_field( $form );
// Get the Amazon Payfort response.
$response = $this->get_amazonpayfort_js_response();
$_POST[ 'input_' . $cc_field->id . '_1' ] = 'XXXXXXXXXXXX' . substr( $response['response']['paymentMethod']['cardNum'], -4 );
}//end of member function
/**
* Validate billing address.
*
* @since 1.0
* @deprecated 2.0
*
* @access public
*
* @param array $address Billing address.
* @param object $field Address field.
*
* @uses GF_Field_Address::get_country_code()
*
* @return bool
*/
public function validate_billing_address( $address, $field ) {
// If address line 1, city or country are missing, return false.
if ( ! rgar( $address, 'addrLine1' ) || ! rgar( $address, 'city' ) || ! rgar( $address, 'country' ) ) {
return false;
}
// Prepare list of countries requiring state and zip code.
//$state_zip_required = array( 'AR', 'AU', 'BG', 'CA', 'CN', 'CY', 'EG', 'ES', 'FR', 'GB', 'ID', 'IN', 'IT', 'JP', 'MX', 'MY', 'NL', 'PA', 'PH', 'PL', 'RO', 'RU', 'RS', 'SE', 'SG', 'TH', 'TR', 'US', 'ZA' );
$state_zip_required = array( 'JOD', 'KWD', 'OMR', 'TND', 'BHD', 'LYD', 'IQD', 'SAR' );
// Get country code.
$country_code = $field->get_country_code( $address['country'] );
// If state or zip code is missing, return false.
if ( in_array( $country_code, $state_zip_required ) && ( ! rgar( $address, 'state' ) || ! rgar( $address, 'zipCode' ) ) ) {
return false;
}
return true;
}//end of member function
}//end of GF_AmazonPayfort class