Plugin Directory

Changeset 3445633


Ignore:
Timestamp:
01/23/2026 02:06:19 PM (4 weeks ago)
Author:
knitpay
Message:

v8.99.4.0:
Razorpay: Added support for Singapore.
Sab Paise: Added support for AES-256 encryption.
UPI/QR: Added UPI Collect Image.
UPI/QR: Moved Knit Pay specific code to Knit Pay UPI plugin.

Location:
knit-pay/trunk
Files:
2 added
9 edited

Legend:

Unmodified
Added
Removed
  • knit-pay/trunk/gateways/razorpay/src/Config.php

    r3220363 r3445633  
    3838    public $refresh_token;
    3939
     40    public $country;
     41
    4042    public $company_name;
    4143
  • knit-pay/trunk/gateways/razorpay/src/Gateway.php

    r3441509 r3445633  
    250250        ];
    251251
     252        if ('sg' === $this->config->country){
     253            $shipping_address = $payment->get_shipping_address();
     254
     255            $razorpay_customer    = $this->call_api( 'customer', 'create', array(
     256                'name' => KnitPayUtils::substr_after_trim( $customer->get_name(), 0, 45 ),
     257                'email' => $customer->get_email(),
     258                'contact'=>$payment->get_billing_address()->get_phone(),
     259                'fail_existing' => false,
     260            ));
     261
     262            $payment->set_meta( 'razorpay_customer_id', $razorpay_customer->id );
     263            $razorpay_order_data['customer_id'] = $razorpay_customer->id;
     264
     265            $razorpay_order_data['customer_details'] = [
     266                'name' => KnitPayUtils::substr_after_trim( $customer->get_name(), 0, 45 ),
     267                'contact' => $payment->get_billing_address()->get_phone(),
     268                'email' => $customer->get_email(),
     269                'shipping_address' => [
     270                    "line1"=> $shipping_address->get_line_1(),
     271                    "line2"=> $shipping_address->get_line_2(),
     272                    "city"=> $shipping_address->get_city(),
     273                    "country"=> 'IN' === $shipping_address->get_country()->get_code()?"IND":"",
     274                    "state"=> KnitPayUtils::get_state_name($shipping_address->get_region(), $shipping_address->get_country()),
     275                    "zipcode"=> (string)$shipping_address->get_postal_code(),
     276                ]
     277            ];
     278        }
     279
    252280        $razorpay_order    = $this->call_api( 'order', 'create', $razorpay_order_data );
    253281        $razorpay_order_id = $razorpay_order['id'];
     
    482510        $razorpay_order_id        = $payment->get_meta( 'razorpay_order_id' );
    483511        $razorpay_subscription_id = $payment->get_meta( 'razorpay_subscription_id' );
     512        $razorpay_customer_id = $payment->get_meta( 'razorpay_customer_id' );
    484513
    485514        $customer        = $payment->get_customer();
     
    525554        ];
    526555
     556        if ('sg' === $this->config->country){
     557            if (!empty($razorpay_customer_id)){
     558                $data['customer_id'] = $razorpay_customer_id;
     559            }
     560
     561            $data['notes'] = [
     562                'invoice_number' => $payment->get_id(),
     563            ];
     564        }
     565
    527566        if ( ! empty( $razorpay_subscription_id ) ) {
    528567            $data['subscription_id'] = $razorpay_subscription_id;
  • knit-pay/trunk/gateways/razorpay/src/Integration.php

    r3436834 r3445633  
    210210        // TODO: Add support for payment link.
    211211
     212        // Country.
     213        $fields[] = [
     214            'section'  => 'general',
     215            'meta_key' => '_pronamic_gateway_razorpay_country',
     216            'title'    => __( 'Country', 'knit-pay-lang' ),
     217            'type'     => 'select',
     218            'options'  => [
     219                'in' => 'India',
     220                'sg' => 'Singapore',
     221            ],
     222            'default'  => 'in',
     223        ];
     224
    212225        // Merchant/Company Name.
    213226        $fields[] = [
     
    341354        $config->access_token                = $this->get_meta( $post_id, 'razorpay_access_token' );
    342355        $config->refresh_token               = $this->get_meta( $post_id, 'razorpay_refresh_token' );
     356        $config->country                     = $this->get_meta( $post_id, 'razorpay_country' );
    343357        $config->company_name                = $this->get_meta( $post_id, 'razorpay_company_name' );
    344358        $config->checkout_image              = Utils::convert_relative_path_to_url( $this->get_meta( $post_id, 'razorpay_checkout_image' ) );
     
    355369        }
    356370        $config->checkout_mode = (int) $config->checkout_mode;
     371
     372        if (empty($config->country)){
     373            $config->country = 'in';
     374        }
    357375
    358376        if ( empty( $config->transaction_fees_percentage ) ) {
  • knit-pay/trunk/gateways/sab-paisa/src/API.php

    r3220363 r3445633  
    1010 *
    1111 * @author Knit Pay
    12  * @version 8.75.3.0
     12 * @version 8.99.4.0
    1313 * @since 8.75.3.0
    1414 */
    15 
    1615class API {
    1716    private $config;
    1817    private $mode;
    1918
    20     private const OPENSSL_CIPHER_NAME = 'aes-128-cbc';
    21     private const CIPHER_KEY_LEN      = 16;
     19    private const CIPHER_KEY_LEN = 16;
     20
     21    // Constants for new encryption method (AES-256-GCM).
     22    private const IV_SIZE     = 12; // 96 bits for GCM.
     23    private const TAG_SIZE    = 16; // 128 bits = 16 bytes.
     24    private const HMAC_LENGTH = 48; // SHA-384 = 48 bytes.
    2225
    2326    public function __construct( Config $config, $mode ) {
     
    2629    }
    2730
     31    /**
     32     * Detect if the key is in new format (base64 encoded)
     33     */
     34    private function is_new_key_format( $key ) {
     35        // Check if it's valid base64 and decodes to 32 bytes (256 bits).
     36        $decoded = base64_decode( $key, true );
     37        if ( $decoded !== false && 32 === strlen( $decoded ) ) {
     38            return true;
     39        }
     40
     41        return false;
     42    }
     43
    2844    public function get_endpoint_url() {
    2945        if ( 'test' === $this->mode ) {
     
    3450    }
    3551
    36     private static function fixKey( $key ) {
     52    private static function fix_key( $key ) {
    3753        if ( strlen( $key ) < self::CIPHER_KEY_LEN ) {
    3854            return str_pad( "$key", self::CIPHER_KEY_LEN, '0' );
    39         }
    40 
    41         if ( strlen( $key ) > self::CIPHER_KEY_LEN ) {
     55        } elseif ( strlen( $key ) > self::CIPHER_KEY_LEN ) {
    4256            return substr( $key, 0, self::CIPHER_KEY_LEN );
    4357        }
     
    87101    }
    88102
     103    /**
     104     * Encrypts the data using the appropriate encryption method based on the key format.
     105     *
     106     * @param string $data The data to encrypt.
     107     * @return string The encrypted data.
     108     */
    89109    private function encrypt( $data ) {
    90110        $key = $this->config->auth_key;
    91111        $iv  = $this->config->auth_iv;
    92112
    93         $encodedEncryptedData = base64_encode( openssl_encrypt( $data, self::OPENSSL_CIPHER_NAME, self::fixKey( $key ), OPENSSL_RAW_DATA, $iv ) );
    94         $encodedIV            = base64_encode( $iv );
    95         $encryptedPayload     = $encodedEncryptedData . ':' . $encodedIV;
    96 
    97         return $encryptedPayload;
    98     }
    99 
     113        // Detect which encryption method to use.
     114        if ( $this->is_new_key_format( $key ) ) {
     115            return $this->encrypt_new( $key, $iv, $data );
     116        } else {
     117            return $this->encrypt_old( $key, $iv, $data );
     118        }
     119    }
     120
     121    // TODO - Remove this method after 1 Jan 2028.
     122    /**
     123     * Old encryption method (AES-128-CBC)
     124     *
     125     * @param string $key  Encryption key.
     126     * @param string $iv   Initialization vector.
     127     * @param string $data Data to encrypt.
     128     * @return string Encrypted data.
     129     */
     130    private function encrypt_old( $key, $iv, $data ) {
     131        $encoded_encrypted_data = base64_encode( openssl_encrypt( $data, 'aes-128-cbc', self::fix_key( $key ), OPENSSL_RAW_DATA, $iv ) );
     132        $encoded_iv             = base64_encode( $iv );
     133        $encrypted_payload      = $encoded_encrypted_data . ':' . $encoded_iv;
     134
     135        return $encrypted_payload;
     136    }
     137
     138    /**
     139     * New encryption method (AES-256-GCM with HMAC-SHA384)
     140     *
     141     * @param string $aes_key_base64  Base64 encoded AES key.
     142     * @param string $hmac_key_base64 Base64 encoded HMAC key.
     143     * @param string $plaintext       Plaintext to encrypt.
     144     * @return string Encrypted data in hex format.
     145     * @throws Exception If encryption fails.
     146     */
     147    private function encrypt_new( $aes_key_base64, $hmac_key_base64, $plaintext ) {
     148        $aes_key  = base64_decode( $aes_key_base64, true );
     149        $hmac_key = base64_decode( $hmac_key_base64, true );
     150
     151        $iv  = random_bytes( self::IV_SIZE );
     152        $tag = '';
     153
     154        $cipher_text = openssl_encrypt(
     155            $plaintext,
     156            'aes-256-gcm',
     157            $aes_key,
     158            OPENSSL_RAW_DATA,
     159            $iv,
     160            $tag,
     161            '',
     162            self::TAG_SIZE
     163        );
     164
     165        if ( false === $cipher_text ) {
     166            throw new Exception( 'Encryption failed.' );
     167        }
     168
     169        $encrypted_message = $iv . $cipher_text . $tag;
     170
     171        // Generate HMAC.
     172        $hmac = hash_hmac( 'sha384', $encrypted_message, $hmac_key, true );
     173
     174        // Final message: [HMAC || IV || CipherText || Tag].
     175        $final_output = $hmac . $encrypted_message;
     176
     177        return strtoupper( bin2hex( $final_output ) );
     178    }
     179
     180    /**
     181     * Decrypts the data using the appropriate decryption method based on the key format.
     182     *
     183     * @param string $data Encrypted data.
     184     * @return string Decrypted data.
     185     */
    100186    private function decrypt( $data ) {
    101187        $key = $this->config->auth_key;
    102 
     188        $iv  = $this->config->auth_iv;
     189
     190        // Detect which decryption method to use.
     191        if ( $this->is_new_key_format( $key ) ) {
     192            return $this->decrypt_new( $key, $iv, $data );
     193        } else {
     194            return $this->decrypt_old( $key, $data );
     195        }
     196    }
     197
     198    // TODO - Remove this method after 1 Jan 2028.
     199    /**
     200     * Old decryption method (AES-128-CBC)
     201     *
     202     * @param string $key  Encryption key.
     203     * @param string $data Encrypted data.
     204     */
     205    private function decrypt_old( $key, $data ) {
    103206        $parts     = explode( ':', $data );
    104207        $encrypted = $parts[0];
    105         // $iv = $parts[1];
    106         $decryptedData = openssl_decrypt( base64_decode( $encrypted ), self::OPENSSL_CIPHER_NAME, self::fixKey( $key ), OPENSSL_RAW_DATA );
    107         return $decryptedData;
    108     }
    109 
     208
     209        $decrypted_data = openssl_decrypt( base64_decode( $encrypted ), 'aes-128-cbc', self::fix_key( $key ), OPENSSL_RAW_DATA );
     210        return $decrypted_data;
     211    }
     212
     213    /**
     214     * New decryption method (AES-256-GCM with HMAC-SHA384)
     215     *
     216     * @param string $aes_key_base64  Base64 encoded AES key.
     217     * @param string $hmac_key_base64 Base64 encoded HMAC key.
     218     * @param string $hex_cipher_text Hex encoded cipher text.
     219     * @return string Decrypted data.
     220     * @throws Exception If decryption fails.
     221     */
     222    private function decrypt_new( $aes_key_base64, $hmac_key_base64, $hex_cipher_text ) {
     223        $aes_key  = base64_decode( $aes_key_base64, true );
     224        $hmac_key = base64_decode( $hmac_key_base64, true );
     225
     226        $full_message = hex2bin( $hex_cipher_text );
     227
     228        $hmac_received  = substr( $full_message, 0, self::HMAC_LENGTH );
     229        $encrypted_data = substr( $full_message, self::HMAC_LENGTH );
     230
     231        $computed_hmac = hash_hmac( 'sha384', $encrypted_data, $hmac_key, true );
     232
     233        if ( ! hash_equals( $hmac_received, $computed_hmac ) ) {
     234            throw new Exception( 'HMAC validation failed. Data may be tampered!' );
     235        }
     236
     237        $iv                   = substr( $encrypted_data, 0, self::IV_SIZE );
     238        $cipher_text_with_tag = substr( $encrypted_data, self::IV_SIZE );
     239        $cipher_text          = substr( $cipher_text_with_tag, 0, -self::TAG_SIZE );
     240        $tag                  = substr( $cipher_text_with_tag, -self::TAG_SIZE );
     241
     242        $plain_text = openssl_decrypt(
     243            $cipher_text,
     244            'aes-256-gcm',
     245            $aes_key,
     246            OPENSSL_RAW_DATA,
     247            $iv,
     248            $tag
     249        );
     250
     251        if ( false === $plain_text ) {
     252            throw new Exception( 'Decryption failed.' );
     253        }
     254
     255        return $plain_text;
     256    }
     257
     258    /**
     259     * Get encrypted data array.
     260     *
     261     * @param array  $data    Data to encrypt.
     262     * @param string $data_key Data key.
     263     * @return array Encrypted data array.
     264     */
    110265    public function get_encrypted_data_array( $data, $data_key = 'encData' ) {
    111266        $data = $this->encrypt( build_query( $data ) );
  • knit-pay/trunk/gateways/upi-qr/src/Gateway.php

    r3441509 r3445633  
    1010use KnitPay\Utils as KnitPayUtils;
    1111use KnitPay\Gateways\PaymentMethods;
     12use Pronamic\WordPress\Http\Facades\Http;
    1213
    1314/**
     
    2223    protected $config;
    2324    private $intent_url_parameters;
    24     protected $payment_expiry_seconds   = 300;
    2525    protected $show_manual_confirmation = false;
    2626    protected $merchant_verified        = false;
     
    8080            $mobile_error = "QR code and Payment Button can't be hidden at the same time. Kindly show at least one of them from the configuration page.";
    8181            throw new Exception( $mobile_error );
    82         }
    83 
    84         if ( PaymentMethods::UPI_COLLECT === $payment->get_payment_method() ) {
    85             $upi_id = $payment->get_meta( 'customer_upi_id' );
    86             if ( empty( $upi_id ) || ! preg_match( '/^[a-zA-Z0-9.\-_]{2,256}@[a-zA-Z]{2,64}$/', $upi_id ) ) {
    87                 throw new Exception( 'Invalid UPI ID format.' );
    88             } elseif ( 2000 < $payment->get_total_amount()->get_minor_units()->format( 0, '.', '' ) ) {
    89                 throw new Exception( 'UPI Collect payment method is only supported for payments up to ₹2000.' );
    90             } elseif ( '5' !== $this->config->payment_template ) {
    91                 throw new Exception( 'UPI Collect payment method is currently only supported with Payment Template 5.' );
    92             }
    9382        }
    9483
     
    256245    }
    257246
    258     public function knit_pay_upi_data( $payment ) {
    259         return [
    260             'knitpay_payment_id'  => $payment->get_id(),
    261             'mode'                => $payment->get_mode(),
    262             'gateway'             => \get_post_meta( $payment->get_config_id(), '_pronamic_gateway_id', true ),
    263             'payment_method'      => $payment->get_payment_method(),
    264             'source'              => $payment->get_source(),
    265             'amount'              => $payment->get_total_amount()->number_format( null, '.', '' ),
    266             'currency'            => $payment->get_total_amount()->get_currency()->get_alphabetic_code(),
    267             'knitpay_version'     => KNITPAY_VERSION,
    268             'knitpay_upi_version' => KNITPAY_UPI_VERSION,
    269             'php_version'         => PHP_VERSION,
    270             'website_url'         => home_url( '/' ),
    271             'data'                => [
    272                 'payment_template' => $this->config->payment_template,
    273             ],
    274         ];
    275     }
    276 
    277247    /**
    278248     * Update status of the specified payment.
     
    317287            $payment = get_pronamic_payment_by_transaction_id( $transaction['utr'] );
    318288            if ( null !== $payment ) {
    319                 continue;
     289                break;
    320290            }
    321291
     
    341311
    342312            $payment->save();
    343         }
    344     }
    345 
    346     protected function make_amount_unique( Payment $payment ) {
    347         $actual_amount_minor = $payment->get_total_amount()->get_minor_units()->to_int();
    348 
    349         if ( get_transient( 'knit_pay_upi_' . $this->config->config_id . '_' . $actual_amount_minor ) ) {
    350             $unique_amount_minor = $this->find_unique_amount( $actual_amount_minor );
    351             $unique_amount       = $unique_amount_minor / 100;
    352             $actual_amount       = $actual_amount_minor / 100;
    353             $payment->add_note( 'Actual amount: ₹' . $actual_amount . '<br>Unique amount generated: ₹' . $unique_amount );
    354 
    355             $unique_money = new \Pronamic\WordPress\Money\Money( $unique_amount, $payment->get_total_amount()->get_currency() );
    356             $payment->set_total_amount( $unique_money );
    357         } else {
    358             $unique_amount_minor = $actual_amount_minor;
    359         }
    360 
    361         // Hold the amount for 15 minutes and can be allocated to other transaction if not paid.
    362         set_transient( 'knit_pay_upi_' . $this->config->config_id . '_' . $unique_amount_minor, $payment->get_id(), 15 * MINUTE_IN_SECONDS );
    363     }
    364 
    365     private function find_unique_amount( $actual_amount_minor, $retry = 1 ) {
    366         if ( $retry > 10 ) {
    367             throw new Exception( 'Unable to generate a unique amount. Please try again after sometime.' );
    368         }
    369 
    370         $range               = $retry ** 2;
    371         $random_amount_minor = rand( -$range, $range ) + $actual_amount_minor; // phpcs:ignore WordPress.WP.AlternativeFunctions.rand_rand
    372 
    373         if ( get_transient( 'knit_pay_upi_' . $this->config->config_id . '_' . $random_amount_minor ) ) {
    374             return $this->find_unique_amount( $actual_amount_minor, ++$retry );
    375         }
    376 
    377         return $random_amount_minor;
    378     }
    379 
    380     public function expire_old_upi_payment( Payment $payment ) {
    381         // Payment can be expired only if the payment current status is Pending.
    382         if ( PaymentStatus::OPEN !== $payment->get_status() ) {
    383             return;
    384         }
    385 
    386         // Make payment status as expired for payment older than 5 min.
    387         $payment_timestamp = $payment->get_date()->getTimestamp();
    388         if ( $this->payment_expiry_seconds < time() - $payment_timestamp ) {
    389             $payment->set_status( PaymentStatus::EXPIRED );
    390 
    391             // Recheck status after 15 min if payment getting expired very soon.
    392             if ( time() - $payment_timestamp < 15 * MINUTE_IN_SECONDS ) {
    393                 \as_schedule_single_action(
    394                     time() + ( 15 * MINUTE_IN_SECONDS ),
    395                     'knit_pay_upi_payment_status_check',
    396                     [
    397                         'payment_id' => $payment->get_id(),
    398                         'gateway_id' => $this->config->gateway_id,
    399                     ],
    400                     'knit-pay-upi'
    401                 );
    402             }
    403313        }
    404314    }
     
    456366    }
    457367
    458     public function get_intent_url_parameters( $payment ) {
     368    protected function get_intent_url_parameters( $payment ) {
    459369        if ( isset( $this->intent_url_parameters ) ) {
    460370            return $this->intent_url_parameters;
     
    469379    }
    470380
    471     public function get_upi_qr_text( $payment ) {
     381    protected function get_upi_qr_text( $payment ) {
    472382        return add_query_arg( $this->get_intent_url_parameters( $payment ), 'upi://pay' );
    473383    }
  • knit-pay/trunk/gateways/upi-qr/src/Integration.php

    r3441509 r3445633  
    2020 */
    2121class Integration extends KnitPayGatewayIntegration {
    22     private $gateway_name;
    23 
    2422    const HIDE_FIELD          = '0';
    2523    const SHOW_FIELD          = '1';
     
    4341
    4442        parent::__construct( $args );
    45 
    46         $this->gateway_name = isset( $args['gateway_name'] ) ? $args['gateway_name'] : $this->get_name();
    47 
    48         // Add Ajax listener.
    49         add_action( 'wp_ajax_nopriv_knit_pay_upi_qr_payment_status_check', [ $this, 'ajax_payment_status_check' ] );
    50         add_action( 'wp_ajax_knit_pay_upi_qr_payment_status_check', [ $this, 'ajax_payment_status_check' ] );
    51 
    52         // Payment status check events are scheduled after payment is expired. We want to check status once more.
    53         add_action( 'knit_pay_upi_payment_status_check', [ $this, 'check_status' ], 10, 2 );
    5443
    5544        // Show notice if Knit Pay UPI supported.
     
    168157    }
    169158
    170     /**
    171      * Get settings fields.
    172      *
    173      * @return array
    174      */
    175     protected function get_pro_about_settings_fields( $fields ) {
    176         // Prerequisite.
    177         $fields[] = [
    178             'section'  => 'general',
    179             'type'     => 'custom',
    180             'title'    => 'Prerequisite',
    181             'callback' => function () {
    182                 $knit_pay_pro_setup_url = admin_url( 'admin.php?page=knit_pay_pro_setup_page' );
    183                 $link                   = '<a target="_blank" href="' . $knit_pay_pro_setup_url . '">' . __( 'Knit Pay >> Knit Pay UPI Setup', 'knit-pay-lang' ) . '</a>';
    184                 $message                = sprintf( __( 'Please visit the %s page to configure "Knit Pay - UPI".', 'knit-pay-lang' ), $link );
    185 
    186                 echo '<ol><li>We rely on the third-party service provider RapidAPI for the setup of Knit Pay UPI. RapidAPI will charge you based on your monthly usage.</li>
    187                         <li>Choose a plan that suits your usage. Each plan includes a certain
    188                             number of free transactions. If you exceed the transaction quota,
    189                             additional charges will apply for receiving extra transactions. <br> <a
    190                             target="_blank"
    191                             href="https://rapidapi.com/knitpay/api/knit-pay-upi/pricing">https://rapidapi.com/knitpay/api/knit-pay-upi/pricing</a></li>
    192                         <li>' . $message . '</li>
    193                     </ol>';
    194             },
    195         ];
    196 
    197         // Terms and Conditions.
    198         $fields[] = [
    199             'section'  => 'general',
    200             'type'     => 'custom',
    201             'title'    => 'Terms and Conditions',
    202             'callback' => function () {
    203                 echo '<ol>
    204                     <li>We generate QR codes on your behalf using your UPI/VPA ID so that you can accept payments with ease.</li>
    205                     <li>Knit Pay UPI is not a payment gateway service, and we are not involved in the payment process in any way. Your Merchant Account will be used for collecting payments.</li>
    206                     <li>We do not collect any of your payments in our account. The payment made by the user through the QR code will be received in your merchant account.</li>
    207                     <li>We are not liable for any fraudulent activity that takes place with your merchant account.</li>
    208                     <li>We might also suspend your RapidAPI subscription if any fraudulent activity gets detected.</li>
    209                     <li>We are not responsible for the suspension of your merchant account due to any reason.</li>
    210                     <li>Use of plugins/codes like these might not be allowed as per the terms of the UPI Service provider. Use this plugin at your own risk.</li>
    211                     <li>Use this plugin at your own risk, we are not liable for any of your losses.</li>
    212                  </ol>';
    213             },
    214         ];
    215 
    216         return $fields;
    217     }
    218 
    219159    public function get_about_settings_fields( $fields ) {
    220160        $fields[] = [
     
    237177
    238178                '<ol>
    239                 <li>Signup at any UPI-enabled App. If you will signup using provided signup URLs and use the referral codes, you might also get a bonus after making few payments.
    240                     <ul>
    241                         <li>- <a target="_blank" href="' . $this->get_url() . 'bharatpe' . $utm_parameter . '">BharatPe (' . $this->get_url() . 'bharatpe)</a> - Signup using the referral link (on the phone) to get ₹200 bonus.</li>
    242                         <li>- <a target="_blank" href="' . $this->get_url() . 'open-money' . $utm_parameter . '">Open Money</a></li>
    243                         <li>- <a target="_blank" href="' . $this->get_url() . 'gpay' . $utm_parameter . '">Google Pay</a> Referral Code: Z05o0</li>
    244                         <li>- <a target="_blank" href="' . $this->get_url() . 'phonepe' . $utm_parameter . '">PhonePe</a></li>
    245                         <li>- <a target="_blank" href="' . $this->get_url() . 'amazon-pay' . $utm_parameter . '">Amazon Pay</a> Referral Code: K1ZESF</li>
    246                         <li>- <a target="_blank" href="https://play.google.com/store/search?q=merchant%20business%20upi&c=apps">More UPI Apps</a></li>
    247                     </ul>
    248                 </li>
    249 
    250                 <li>Link your Bank Account and generate a UPI ID/VPA.</li>
    251 
    252                 <li>Use this VPA/UPI ID on the configuration page below.
    253                 <br><strong>Kindly use the correct VPA/UPI ID. In case of wrong settings, payments will get credited to the wrong bank account. Knit Pay will not be responsible for any of your lose.</strong></li>
    254 
    255                 <li>Save the settings.</li>
    256 
    257                 <li>Before going live, make a test payment of ₹1 and check that you are receiving this payment in the correct bank account.</li>
    258 
    259                 </ol>';
     179                    <li>Signup at any UPI-enabled App. If you will signup using provided signup URLs and use the referral codes, you might also get a bonus after making few payments.
     180                        <ul>
     181                            <li>- <a target="_blank" href="' . $this->get_url() . 'bharatpe' . $utm_parameter . '">BharatPe (' . $this->get_url() . 'bharatpe)</a> - Signup using the referral link (on the phone) to get ₹200 bonus.</li>
     182                            <li>- <a target="_blank" href="' . $this->get_url() . 'open-money' . $utm_parameter . '">Open Money</a></li>
     183                            <li>- <a target="_blank" href="' . $this->get_url() . 'gpay' . $utm_parameter . '">Google Pay</a> Referral Code: Z05o0</li>
     184                            <li>- <a target="_blank" href="' . $this->get_url() . 'phonepe' . $utm_parameter . '">PhonePe</a></li>
     185                            <li>- <a target="_blank" href="' . $this->get_url() . 'amazon-pay' . $utm_parameter . '">Amazon Pay</a> Referral Code: K1ZESF</li>
     186                            <li>- <a target="_blank" href="https://play.google.com/store/search?q=merchant%20business%20upi&c=apps">More UPI Apps</a></li>
     187                        </ul>
     188                    </li>
     189
     190                    <li>Link your Bank Account and generate a UPI ID/VPA.</li>
     191
     192                    <li>Use this VPA/UPI ID on the configuration page below.
     193                    <br><strong>Kindly use the correct VPA/UPI ID. In case of wrong settings, payments will get credited to the wrong bank account. Knit Pay will not be responsible for any of your lose.</strong></li>
     194
     195                    <li>Save the settings.</li>
     196
     197                    <li>Before going live, make a test payment of ₹1 and check that you are receiving this payment in the correct bank account.</li>
     198
     199                    </ol>';
    260200            },
    261201        ];
     
    269209
    270210                '<ol>
    271                 <li>On the payment screen, the customer scans the QR code using any UPI-enabled mobile app and makes the payment.</li>
    272 
    273                 <li>The customer enters the transaction ID and submits the payment form.</li>
    274 
    275                 <li>Payment remains on hold. Merchant manually checks the payment and mark it as complete on the "Knit Pay" Payments page.</li>
    276 
    277                 <li>Automatic tracking is not available in the UPI QR payment method. You can signup at other supported free payment gateways to get an automatic payment tracking feature.
    278                     <br><a target="_blank" href="https://www.knitpay.org/indian-payment-gateways-supported-in-knit-pay/">Indian Payment Gateways Supported in Knit Pay</a>
    279                 </li>
    280 
    281             </ol>';},
     211                        <li>On the payment screen, the customer scans the QR code using any UPI-enabled mobile app and makes the payment.</li>
     212
     213                        <li>The customer enters the transaction ID and submits the payment form.</li>
     214
     215                        <li>Payment remains on hold. Merchant manually checks the payment and mark it as complete on the "Knit Pay" Payments page.</li>
     216
     217                        <li>Automatic tracking is not available in the UPI QR payment method. You can signup at other supported free payment gateways to get an automatic payment tracking feature.
     218                            <br><a target="_blank" href="https://www.knitpay.org/indian-payment-gateways-supported-in-knit-pay/">Indian Payment Gateways Supported in Knit Pay</a>
     219                        </li>
     220                    </ol>';
     221            },
    282222        ];
    283223
     
    352292            'type'        => 'text',
    353293            'classes'     => [ 'regular-text', 'code' ],
     294            'default'     => '0000',
    354295            'tooltip'     => __( 'four-digit ISO 18245 merchant category code (MCC) to classify your business.', 'knit-pay-lang' ),
    355296            'description' => 'You can refer to below links to find out your MCC.<br>' .
     
    463404    }
    464405
    465     protected function get_connection_status_field( $fields ) {
    466         try {
    467             $this->is_connected();
    468 
    469             $fields[] = [
    470                 'section'  => 'general',
    471                 'type'     => 'custom',
    472                 'title'    => 'Connected',
    473                 'callback' => function () {
    474                     echo '<span class="dashicons dashicons-yes"></span>';
    475                 },
    476             ];
    477         } catch ( Exception $e ) {
    478             $fields[] = [
    479                 'section'  => 'general',
    480                 'type'     => 'custom',
    481                 'title'    => 'Connected',
    482                 'callback' => function () {
    483                     echo '<span class="dashicons dashicons-no"></span>';
    484                 },
    485             ];
    486 
    487             $message = '<strong>Knit Pay</strong> - The connection to ' . $this->gateway_name . ' could not be established.';
    488             wp_admin_notice( $message, [ 'type' => 'error' ] );
    489         }
    490 
    491         return $fields;
    492     }
    493 
    494406    protected function get_supported_template_list() {
    495407        return [
     
    501413    }
    502414
     415    /**
     416     * Get config.
     417     *
     418     * @param int $post_id Post ID.
     419     * @return Config
     420     */
    503421    public function get_config( $post_id ) {
    504422        $config = new Config();
     
    619537        );
    620538    }
    621 
    622     protected function is_connected() {
    623         return true;
    624     }
    625 
    626     /**
    627      * Check status of the specified payment.
    628      *
    629      * @param int         $payment_id The payment ID to check.
    630      * @param string|null $gateway_id The gateway ID to check against.
    631      * @return void
    632      */
    633     public function check_status( $payment_id = null, $gateway_id = null ) {
    634         // Check only if this payment belongs to this gateway.
    635         if ( $gateway_id !== $this->get_id() ) {
    636             return;
    637         }
    638 
    639         $payment = get_pronamic_payment( $payment_id );
    640 
    641         // No payment found, unable to check status.
    642         if ( null === $payment ) {
    643             return;
    644         }
    645 
    646         // Add note.
    647         $note = sprintf(
    648             /* translators: %s: Knit Pay UPI */
    649             __( 'Payment status check at gateway by %s.', 'knit-pay-lang' ),
    650             __( 'Knit Pay UPI', 'knit-pay-lang' )
    651         );
    652 
    653         $payment->add_note( $note );
    654 
    655         // Update payment.
    656         Plugin::update_payment( $payment, false );
    657     }
    658 
    659     public function ajax_payment_status_check() {
    660         $payment_id     = isset( $_POST['knit_pay_payment_id'] ) ? sanitize_text_field( $_POST['knit_pay_payment_id'] ) : '';
    661         $transaction_id = isset( $_POST['knit_pay_transaction_id'] ) ? sanitize_text_field( $_POST['knit_pay_transaction_id'] ) : '';
    662         $knit_pay_nonce = isset( $_POST['knit_pay_nonce'] ) ? sanitize_text_field( $_POST['knit_pay_nonce'] ) : '';
    663 
    664         $nonce_action = "knit_pay_payment_status_check|{$payment_id}|{$transaction_id}";
    665 
    666         if ( ! wp_verify_nonce( $knit_pay_nonce, $nonce_action ) ) {
    667             wp_send_json_error( __( 'Nonce Missmatch!', 'knit-pay-lang' ) );
    668         }
    669 
    670         $payment = get_pronamic_payment( $payment_id );
    671 
    672         if ( null === $payment ) {
    673             exit;
    674         }
    675 
    676         $gateway = $payment->get_gateway();
    677         if ( ! $gateway->supports( 'payment_status_request' ) ) {
    678             wp_send_json_error( __( 'Gateway does not support automatic payment status check.', 'knit-pay-lang' ) );
    679         }
    680 
    681         // Update status.
    682         try {
    683             $gateway->update_status( $payment );
    684 
    685             // Save New Status.
    686             if ( PaymentStatus::OPEN !== $payment->get_status() ) {
    687                 $payment->save();
    688             }
    689 
    690             wp_send_json_success( $payment->get_status() );
    691         } catch ( \Exception $error ) {
    692             $message = $error->getMessage();
    693 
    694             // Maybe include error code in message.
    695             $code = $error->getCode();
    696 
    697             if ( $code > 0 ) {
    698                 $message = \sprintf( '%s: %s', $code, $message );
    699             }
    700 
    701             // Add note.
    702             $payment->add_note( $message );
    703 
    704             wp_send_json_error( $message );
    705         }
    706     }
    707539}
  • knit-pay/trunk/includes/Utils.php

    r3436834 r3445633  
    44
    55use Pronamic\WordPress\Pay\Country;
     6use Pronamic\WordPress\Pay\Region;
    67use Pronamic\WordPress\Pay\ContactNameHelper;
    78use Exception;
     
    2021
    2122        if ( ! empty( $country->get_name() ) ) {
    22             $country->get_name();
     23            return $country->get_name();
    2324        }
    2425       
     
    279280        }
    280281    }
     282
     283    public static function get_state_name( ?Region $region, ?Country $country ) {
     284        if (!function_exists('WC')){
     285            return $region->get_code();
     286        }
     287        $country_code = $country->get_code();
     288        $state_code   = empty($region->get_code()) ? $region->get_value() : $region->get_code();
     289
     290        $countries = WC()->countries; // Get an instance of the WC_Countries Object
     291        $country_states = $countries->get_states( $country_code ); // Get the states array for the specific country
     292
     293        if ( isset( $country_states[ $state_code ] ) ) {
     294            return $country_states[ $state_code ]; // Return the full state name
     295        } else {
     296            return $state_code; // Return the code itself if the full name is not found (e.g., if the user typed it manually)
     297        }
     298    }
    281299   
    282300    /*
  • knit-pay/trunk/knit-pay.php

    r3441509 r3445633  
    55 * Description: Seamlessly integrates 500+ payment gateways, including Cashfree, Instamojo, Razorpay, Paypal, Stripe, UPI QR, GoUrl, and SSLCommerz, with over 100 WordPress plugins.
    66 *
    7  * Version: 8.99.3.0
     7 * Version: 8.99.4.0
    88 * Requires at least: 6.2
    99 * Requires PHP: 8.1
  • knit-pay/trunk/readme.txt

    r3441509 r3445633  
    190190* Sadad
    191191* Omise
     192* Nuvei
     193* AsiaPay
     194* Paymob
    192195* and many more...
    193196
Note: See TracChangeset for help on using the changeset viewer.