898 lines
35 KiB
PHP
898 lines
35 KiB
PHP
<?php
|
||
namespace Formipay\Integration;
|
||
use Formipay\Traits\SingletonTrait;
|
||
use Formipay\Payment\Payment;
|
||
// Exit if accessed directly
|
||
if ( ! defined( 'ABSPATH' ) ) exit;
|
||
|
||
class Paypal extends Payment {
|
||
|
||
use SingletonTrait;
|
||
|
||
private $gateway = 'paypal';
|
||
|
||
protected function __construct() {
|
||
|
||
parent::__construct();
|
||
|
||
add_action( 'init', [$this, 'create_db'] );
|
||
add_action( 'rest_api_init', [$this, 'webhook_endpoint'] );
|
||
add_action( 'formipay/order/new', [$this, 'add_transaction'], 99 );
|
||
|
||
add_filter( 'formipay/form-config/tab:payments/gateways', [$this, 'add_gateway'] );
|
||
add_filter( 'formipay/form-config/tab:payments', [$this, 'add_paypal_settings'] );
|
||
add_filter( 'formipay/global-settings/tab:payment', [$this, 'add_settings'] );
|
||
add_filter( 'formipay/payment-list/paypal/label', [$this, 'set_frontend_label'] );
|
||
add_filter( 'formipay/payment-list/paypal/logo', [$this, 'set_frontend_logo'] );
|
||
add_filter( 'formipay/order/result', [$this, 'process_payment'], 99 );
|
||
add_action( 'parse_query', [$this, 'check_parse_query'] );
|
||
add_filter( 'formipay/order/payment-details/paypal', [$this, 'render_payment_details'], 999, 4 );
|
||
|
||
add_filter('formipay/notification/order/email/action-button', [$this, 'render_email_action_button'], 99, 3 );
|
||
|
||
}
|
||
|
||
public function create_db() {
|
||
|
||
global $wpdb;
|
||
$charset_collate = $wpdb->get_charset_collate();
|
||
|
||
$create[] = "CREATE TABLE `{$wpdb->base_prefix}formipay_paypal_trx` (
|
||
`id` bigint(20) NOT NULL AUTO_INCREMENT,
|
||
`created_date` datetime DEFAULT CURRENT_TIMESTAMP,
|
||
`form_id` int DEFAULT 0,
|
||
`order_id` int DEFAULT 0,
|
||
`currency_code` text,
|
||
`total` float(10, 2) DEFAULT 0,
|
||
`status` text,
|
||
`meta_data` text,
|
||
PRIMARY KEY (`id`)
|
||
) $charset_collate;";
|
||
|
||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||
|
||
dbDelta($create);
|
||
|
||
}
|
||
|
||
public function webhook_endpoint() {
|
||
register_rest_route('formipay/v1', '/paypal-webhook', [
|
||
'methods' => 'POST',
|
||
'callback' => [$this, 'handle_paypal_webhook'],
|
||
'permission_callback' => '__return_true'
|
||
]);
|
||
}
|
||
|
||
public function add_transaction($order_data) {
|
||
|
||
$form_id = absint($order_data['form_id'] ?? 0);
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
$timeout = isset($formipay_settings['paypal_timeout']) ? (int) $formipay_settings['paypal_timeout'] : 1440;
|
||
|
||
$currency_meta = formipay_get_post_meta($form_id, 'product_currency');
|
||
$currency_parts = explode(':::', $currency_meta);
|
||
$currency = !empty($currency_parts[0]) ? sanitize_text_field($currency_parts[0]) : '';
|
||
|
||
if($this->gateway === ($order_data['payment_gateway'] ?? '')) {
|
||
|
||
$submit_args = [
|
||
'created_date' => formipay_date('Y-m-d H:i:s', strtotime($order_data['created_date'] ?? 'now')),
|
||
'form_id' => $form_id,
|
||
'order_id' => absint($order_data['id'] ?? 0),
|
||
'currency_code' => $currency,
|
||
'total' => floatval($order_data['total'] ?? 0),
|
||
'status' => 'PENDING',
|
||
'meta_data' => maybe_serialize([
|
||
'request_response' => maybe_serialize($order_data['payment_callback']),
|
||
'payer_action_url' => esc_url_raw($order_data['redirect_url'] ?? '')
|
||
])
|
||
];
|
||
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'formipay_paypal_trx';
|
||
|
||
$format = [
|
||
'%s', // created_date
|
||
'%d', // form_id
|
||
'%d', // order_id
|
||
'%s', // currency_code
|
||
'%f', // total
|
||
'%s', // status
|
||
'%s' // meta_data
|
||
];
|
||
|
||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
|
||
$wpdb->insert($table, $submit_args, $format);
|
||
|
||
wp_schedule_single_event(
|
||
(int) formipay_date('timestamp') + $timeout, 'formipay/order/payment-timeout', [
|
||
'order_id' => $order_data['id'],
|
||
'payment_gateway' => 'paypal'
|
||
]
|
||
);
|
||
}
|
||
}
|
||
|
||
public function auto_cancel_order_on_timeout($order_id, $payment_gateway) {
|
||
|
||
if($payment_gateway == 'paypal'){
|
||
|
||
global $wpdb;
|
||
$order = formipay_get_order($order_id);
|
||
if($order['status'] !== 'completed'){
|
||
Order::update($order_id, [
|
||
'status' => 'cancelled'
|
||
]);
|
||
$this->cancel_payment($order_id);
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
public function get_transaction($order_id = 0) {
|
||
|
||
global $wpdb;
|
||
$table = $wpdb->prefix .'formipay_paypal_trx';
|
||
if($order_id == 0){
|
||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||
$get = $wpdb->get_results(
|
||
$wpdb->prepare(
|
||
"SELECT * FROM %i", $table
|
||
)
|
||
);
|
||
}else{
|
||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||
$get = $wpdb->get_row(
|
||
$wpdb->prepare(
|
||
"SELECT * FROM %i WHERE `order_id` = %d", $table, $order_id
|
||
)
|
||
);
|
||
}
|
||
|
||
return $get;
|
||
|
||
}
|
||
|
||
public function update_transaction($order_id, $status, $callback) {
|
||
|
||
$args = [
|
||
'status' => sanitize_text_field( $status ),
|
||
'meta_data' => maybe_serialize( $callback )
|
||
];
|
||
|
||
global $wpdb;
|
||
$table = $wpdb->prefix . 'formipay_paypal_trx';
|
||
$where = ['order_id' => $order_id];
|
||
|
||
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
|
||
$update = $wpdb->update(
|
||
$table, $args, $where
|
||
);
|
||
|
||
return $update;
|
||
|
||
}
|
||
|
||
public function add_gateway($gateways) {
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
if(isset($formipay_settings['paypal_toggle']) && $formipay_settings['paypal_toggle'] == 'yes') {
|
||
$gateways[] = [
|
||
'id' => $this->gateway,
|
||
'gateway' => __( 'Paypal', 'formipay' )
|
||
];
|
||
}
|
||
|
||
return $gateways;
|
||
|
||
}
|
||
|
||
public function add_settings($fields) {
|
||
|
||
$payment_instruction = file_get_contents( FORMIPAY_PATH . 'admin/templates/paypal-instruction.html' );
|
||
|
||
$paypal_settings = array(
|
||
$this->gateway.'_toggle' => array(
|
||
'type' => 'checkbox',
|
||
'label' => __( 'Activate Paypal', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' )
|
||
),
|
||
$this->gateway.'_account_group' => array(
|
||
'type' => 'group_title',
|
||
'label' => __( 'Paypal Account', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
),
|
||
'group' => 'started'
|
||
),
|
||
$this->gateway.'_instruction' => array(
|
||
'type' => 'notification_message',
|
||
'description' => $payment_instruction,
|
||
'buttons' => array(
|
||
'show_instruction' => array(
|
||
'text' => __( 'Show Instruction', 'formipay' ),
|
||
'class' => 'show-instruction'
|
||
)
|
||
),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
),
|
||
'submenu' => __( 'Paypal', 'formipay' )
|
||
),
|
||
$this->gateway.'_client_id' => array(
|
||
'type' => 'text',
|
||
'label' => __( 'Client ID', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
)
|
||
),
|
||
$this->gateway.'_secret' => array(
|
||
'type' => 'text',
|
||
'label' => __( 'Secret', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
)
|
||
),
|
||
$this->gateway.'_environment' => array(
|
||
'type' => 'select',
|
||
'label' => __( 'Environment', 'formipay' ),
|
||
'options' => array(
|
||
'sandbox' => __( 'Sandbox', 'formipay' ),
|
||
'production' => __( 'Production', 'formipay' )
|
||
),
|
||
'value' => 'sandbox',
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
)
|
||
),
|
||
$this->gateway.'_merchant_email' => array(
|
||
'type' => 'text',
|
||
'label' => __( 'Merchant Email', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
),
|
||
),
|
||
$this->gateway.'_timeout' => array(
|
||
'type' => 'number',
|
||
'label' => __( 'Payment Timeout (minute)', 'formipay' ),
|
||
'description' => __( 'Set a timeout to wait for payment. After this timeout is up, the order status automatically changes to cancelled.', 'formipay' ),
|
||
'value' => 120,
|
||
'group' => 'ended',
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'dependency' => array(
|
||
'key' => $this->gateway.'_toggle',
|
||
'value' => 'not_empty'
|
||
),
|
||
'group' => 'ended'
|
||
),
|
||
);
|
||
|
||
foreach($paypal_settings as $key => $value) {
|
||
$fields[$key] = $value;
|
||
}
|
||
|
||
return $fields;
|
||
|
||
}
|
||
|
||
public function add_paypal_settings($fields) {
|
||
|
||
$paypal_settings = array(
|
||
|
||
$this->gateway.'_confirmation_notice_message_group' => array(
|
||
'type' => 'group_title',
|
||
'label' => __( 'Confirmation Notice Messages', 'formipay' ),
|
||
'description' => __(
|
||
'Set notice message on every condition about attempting to confirm the payment', 'formipay' ),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'group' => 'started'
|
||
),
|
||
$this->gateway.'_confirmation_update_to_status' => array(
|
||
'type' => 'select',
|
||
'label' => __( 'Change status order to', 'formipay' ),
|
||
'options' => formipay_order_status_list(),
|
||
'value' => 'payment-confirm',
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
),
|
||
$this->gateway.'_confirmation_message_success' => array(
|
||
'type' => 'hint_textarea',
|
||
'label' => __( 'Successfully Confirmed Message', 'formipay' ),
|
||
'hints' => array(
|
||
'order_id' => __('Order ID', 'formipay')
|
||
),
|
||
'value' => __('Successfully confirmed payment for #{{order_id}}', 'formipay'),
|
||
'description' => __('When successfully change the order status to the preferred one', 'formipay'),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
),
|
||
$this->gateway.'_confirmation_message_already_confirmed' => array(
|
||
'type' => 'hint_textarea',
|
||
'label' => __( 'Has Been Confirmed Message', 'formipay' ),
|
||
'hints' => array(
|
||
'order_id' => __('Order ID', 'formipay')
|
||
),
|
||
'value' => __('#{{order_id}} has been confirmed', 'formipay'),
|
||
'description' => __('When the order status has been the preferred one', 'formipay'),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
),
|
||
$this->gateway.'_confirmation_message_error' => array(
|
||
'type' => 'hint_textarea',
|
||
'label' => __( 'Failed to Confirm Message', 'formipay' ),
|
||
'hints' => array(
|
||
'order_id' => __('Order ID', 'formipay'),
|
||
'system_error_message' => __('System Error Message', 'formipay')
|
||
),
|
||
'value' => __('Failed to proceed. Error: {{system_error_message}}', 'formipay'),
|
||
'description' => __('When error to change the order status to the preferred one', 'formipay'),
|
||
'submenu' => __( 'Paypal', 'formipay' ),
|
||
'group' => 'ended'
|
||
)
|
||
);
|
||
|
||
foreach($paypal_settings as $key => $value) {
|
||
$fields[$key] = $value;
|
||
}
|
||
|
||
return $fields;
|
||
|
||
}
|
||
|
||
public function set_frontend_label($label){
|
||
|
||
$label = __( 'Paypal', 'formipay' );
|
||
|
||
return $label;
|
||
|
||
}
|
||
|
||
public function set_frontend_logo($logo){
|
||
|
||
if($logo == false){
|
||
$logo = FORMIPAY_URL . 'public/assets/img/paypal.png';
|
||
}
|
||
|
||
return $logo;
|
||
|
||
}
|
||
|
||
public function prepare_payload($order_data) {
|
||
|
||
// error_log('current order'. $order_data['id'] . ': ' . print_r($order_data, true));
|
||
|
||
$form_id = intval($order_data['form_id']);
|
||
$order_details = $order_data['items'];
|
||
|
||
if($this->gateway == $order_data['payment_gateway']){
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
$grand_total = floatval($order_data['total']);
|
||
|
||
// Currency
|
||
$currency = explode(':::', formipay_get_post_meta($form_id, 'product_currency'));
|
||
$currency = $currency[0];
|
||
|
||
$currency_decimal_digits = formipay_get_post_meta($form_id, 'product_currency_decimal_digits');
|
||
$currency_decimal_symbol = formipay_get_post_meta($form_id, 'product_currency_decimal_symbol');
|
||
$currency_thousand_separator = formipay_get_post_meta($form_id, 'product_currency_thousand_separator');
|
||
|
||
$slug = 'thankyou';
|
||
$formipay_settings = get_option('formipay_settings');
|
||
if(!empty($formipay_settings['thankyou_link'])){
|
||
$slug = $formipay_settings['thankyou_link'];
|
||
}
|
||
|
||
if(formipay_get_post_meta($form_id, 'product_type') == 'digital'){
|
||
$shipping_preference = 'NO_SHIPPING';
|
||
}else{
|
||
$shipping_preference = 'SET_PROVIDED_ADDRESS';
|
||
}
|
||
|
||
// Setup items
|
||
$items = [];
|
||
foreach($order_details as $item){
|
||
$qty = 1;
|
||
if(!empty($item['qty'])){
|
||
$qty = $item['qty'];
|
||
}
|
||
if(floatval($item['amount']) >= 0){
|
||
$items[] = [
|
||
'name' => $item['item'],
|
||
'unit_amount' => [
|
||
'currency_code' => $currency,
|
||
'value' => floatval($item['amount'])
|
||
],
|
||
'quantity' => $qty
|
||
];
|
||
}
|
||
}
|
||
|
||
$items = apply_filters('formipay/order/paypal/payload/items', $items, $currency, $order_data);
|
||
|
||
$breakdown = array(
|
||
"item_total" => array(
|
||
"currency_code" => $currency,
|
||
"value" => $grand_total
|
||
)
|
||
);
|
||
$breakdown = apply_filters('formipay/order/paypal/payload/breakdown', $breakdown, $currency, $order_data);
|
||
|
||
$unique_id = $order_data['meta_data']['session_id']['value'];
|
||
|
||
$thankyou_link = 'thankyou';
|
||
if(isset($formipay_settings['thankyou_link']) && !empty($formipay_settings['thankyou_link'])){
|
||
$thankyou_link = $formipay_settings['thankyou_link'];
|
||
}
|
||
$token_manager = new \Formipay\Token();
|
||
$token = $token_manager->generate(
|
||
$order_data['id'],
|
||
$form_id,
|
||
900 // 15-minute expiration
|
||
);
|
||
$access_link = site_url('/'.$thankyou_link.'/' . $token);
|
||
|
||
// Prepare the payload
|
||
$payload = json_encode(array(
|
||
"intent" => "CAPTURE",
|
||
"purchase_units" => array(
|
||
array(
|
||
"custom_id" => (string)$order_data['id'],
|
||
"amount" => array(
|
||
"currency_code" => $currency,
|
||
"value" => $grand_total, // Format amount
|
||
"breakdown" => $breakdown
|
||
),
|
||
"items" => $items
|
||
)
|
||
),
|
||
'payment_source' => array(
|
||
'paypal' => array(
|
||
'experience_context' => array(
|
||
'return_url' => $access_link . '?paypal-status=success',
|
||
'cancel_url' => $access_link . '?paypal-status=cancel',
|
||
'user_action' => 'PAY_NOW',
|
||
'brand_name' => 'FORMIPAY INC',
|
||
'local' => 'en-US',
|
||
'landing_page' => 'LOGIN',
|
||
'payment_method_preference' => 'IMMEDIATE_PAYMENT_REQUIRED',
|
||
'shipping_preference' => $shipping_preference
|
||
)
|
||
)
|
||
)
|
||
));
|
||
|
||
return $payload;
|
||
}
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
public function process_payment( $order_data ) {
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
if($order_data['payment_gateway'] == 'paypal') {
|
||
|
||
$paypal_client_id = $formipay_settings['paypal_client_id'];
|
||
$paypal_secret = $formipay_settings['paypal_secret'];
|
||
|
||
// Set your authentication header
|
||
$auth = base64_encode($paypal_client_id . ':' . $paypal_secret);
|
||
|
||
// Define arguments for wp_remote_post
|
||
$args = array(
|
||
'headers' => array(
|
||
'Content-Type' => 'application/json',
|
||
'Authorization' => 'Basic ' . $auth,
|
||
),
|
||
'method' => 'POST',
|
||
'data_format' => 'body'
|
||
);
|
||
|
||
$url = 'https://api-m.sandbox.paypal.com/v2';
|
||
if(isset($formipay_settings[$this->gateway.'_environment']) && $formipay_settings[$this->gateway.'_environment'] == 'production') {
|
||
$url = 'https://api-m.paypal.com/v2';
|
||
}
|
||
|
||
$payload = $this->prepare_payload($order_data);
|
||
// error_log('paypal payload:'.print_r($payload, true));
|
||
$args['body'] = $payload;
|
||
$url = $url.'/checkout/orders';
|
||
|
||
$response = wp_remote_retrieve_body( wp_remote_post($url, $args) );
|
||
$response = json_decode($response);
|
||
// error_log('paypal response:'.print_r($response, true));
|
||
|
||
$payer_action_link = null;
|
||
|
||
foreach ($response->links as $link) {
|
||
if ($link->rel === 'payer-action') {
|
||
$payer_action_link = $link->href;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if($payer_action_link !== null){
|
||
$order_data['payment_callback'] = $response;
|
||
$order_data['redirect_url'] = $payer_action_link;
|
||
}
|
||
|
||
}
|
||
|
||
return $order_data;
|
||
|
||
}
|
||
|
||
public function cancel_payment( $order_data ) {
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
if($order_data['payment_gateway'] == 'paypal') {
|
||
|
||
$paypal_client_id = $formipay_settings['paypal_client_id'];
|
||
$paypal_secret = $formipay_settings['paypal_secret'];
|
||
|
||
// Set your authentication header
|
||
$auth = base64_encode($paypal_client_id . ':' . $paypal_secret);
|
||
|
||
// Define arguments for wp_remote_post
|
||
$args = array(
|
||
'headers' => array(
|
||
'Content-Type' => 'application/json',
|
||
'Authorization' => 'Basic ' . $auth,
|
||
),
|
||
'method' => 'POST',
|
||
'data_format' => 'body'
|
||
);
|
||
|
||
$url = 'https://api-m.sandbox.paypal.com/v2';
|
||
if(isset($formipay_settings[$this->gateway.'_environment']) && $formipay_settings[$this->gateway.'_environment'] == 'production') {
|
||
$url = 'https://api-m.paypal.com/v2';
|
||
}
|
||
|
||
$payload = $this->prepare_payload($order_data);
|
||
// error_log('paypal payload:'.print_r($payload, true));
|
||
$args['body'] = $payload;
|
||
$url = $url.'/checkout/orders';
|
||
|
||
$response = wp_remote_retrieve_body( wp_remote_post($url, $args) );
|
||
$response = json_decode($response);
|
||
// error_log('paypal response:'.print_r($response, true));
|
||
|
||
$payer_action_link = null;
|
||
|
||
foreach ($response->links as $link) {
|
||
if ($link->rel === 'payer-action') {
|
||
$payer_action_link = $link->href;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if($payer_action_link !== null){
|
||
$order_data['redirect_url'] = $payer_action_link;
|
||
}
|
||
|
||
do_action('formipay/notification/order', $order_data);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
public function check_parse_query() {
|
||
if (is_admin()) {
|
||
return;
|
||
}
|
||
|
||
global $wp_query;
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
// Validate query vars early
|
||
$query = $wp_query->query;
|
||
if (
|
||
is_array($query) &&
|
||
!empty($query['formipay-payment-confirm']) &&
|
||
filter_var($query['formipay-payment-confirm'], FILTER_VALIDATE_BOOLEAN) &&
|
||
!empty($query['gateway']) &&
|
||
$query['gateway'] === 'paypal'
|
||
) {
|
||
// Sanitize and validate token
|
||
$token_raw = isset($query['formipay-token']) ? $query['formipay-token'] : '';
|
||
$token_decoded = base64_decode($token_raw, true);
|
||
if (!$token_decoded) {
|
||
return;
|
||
}
|
||
$token = explode(':::', $token_decoded);
|
||
if (count($token) < 2) {
|
||
return;
|
||
}
|
||
$form_id = absint($token[0]);
|
||
$order_id = absint($token[1]);
|
||
if (!$form_id || !$order_id) {
|
||
return;
|
||
}
|
||
|
||
$check_order = $this->get_transaction($order_id);
|
||
|
||
// Check PayPal status and process payment
|
||
if (!empty($check_order) &&
|
||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||
isset($_GET['paypal-status']) && sanitize_text_field(wp_unslash($_GET['paypal-status'])) === 'success' &&
|
||
strtoupper($check_order->status) === 'PENDING'
|
||
) {
|
||
$paypal_client_id = isset($formipay_settings['paypal_client_id']) ? $formipay_settings['paypal_client_id'] : '';
|
||
$paypal_secret = isset($formipay_settings['paypal_secret']) ? $formipay_settings['paypal_secret'] : '';
|
||
$environment = isset($formipay_settings[$this->gateway . '_environment']) ? $formipay_settings[$this->gateway . '_environment'] : 'sandbox';
|
||
|
||
if ($paypal_client_id && $paypal_secret) {
|
||
$auth = base64_encode($paypal_client_id . ':' . $paypal_secret);
|
||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
|
||
$paypal_order_id = isset($_GET['token']) ? sanitize_text_field(wp_unslash($_GET['token'])) : '';
|
||
|
||
$base_url = ($environment === 'production')
|
||
? 'https://api-m.paypal.com/v2'
|
||
: 'https://api-m.sandbox.paypal.com/v2';
|
||
|
||
$url = $base_url . '/checkout/orders/' . $paypal_order_id . '/capture';
|
||
|
||
$args = [
|
||
'headers' => [
|
||
'Content-Type' => 'application/json',
|
||
'Authorization' => 'Basic ' . $auth,
|
||
],
|
||
'method' => 'POST',
|
||
'data_format' => 'body',
|
||
'timeout' => 20,
|
||
];
|
||
|
||
$response = wp_remote_post($url, $args);
|
||
$body = is_wp_error($response) ? null : wp_remote_retrieve_body($response);
|
||
$response_data = json_decode($body, true);
|
||
|
||
if (isset($response_data['status']) && $response_data['status'] === 'COMPLETED') {
|
||
$update_order_status = formipay_update_order_status([
|
||
'form_id' => $form_id,
|
||
'order_id' => $order_id,
|
||
'payment_gateway' => $this->gateway,
|
||
'status' => 'completed'
|
||
]);
|
||
if (!empty($update_order_status['valid'])) {
|
||
$capture_details = $response_data['purchase_units'][0]['payments']['captures'][0] ?? [];
|
||
$this->update_transaction($order_id, $response_data['status'], $capture_details);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Determine redirect action
|
||
$action_type = formipay_get_post_meta($form_id, 'submit_action_type');
|
||
$params = isset($_SERVER['QUERY_STRING']) ? sanitize_text_field(wp_unslash($_SERVER['QUERY_STRING'])) : '';
|
||
|
||
if ($action_type === 'thankyou') {
|
||
$thankyou_slug = !empty($formipay_settings['thankyou_link'])
|
||
? sanitize_title($formipay_settings['thankyou_link'])
|
||
: 'thankyou';
|
||
wp_safe_redirect(site_url('/' . $thankyou_slug . '/' . $token_raw . '?' . $params));
|
||
exit;
|
||
} elseif ($action_type === 'redirect') {
|
||
$url = formipay_get_post_meta($form_id, 'redirect_url');
|
||
$url = esc_url_raw($url);
|
||
if (strpos($url, '?') !== false) {
|
||
wp_safe_redirect($url . '&' . $params);
|
||
} else {
|
||
wp_safe_redirect($url . '?' . $params);
|
||
}
|
||
exit;
|
||
} elseif ($action_type === 'whatsapp') {
|
||
$order = formipay_get_order($order_id);
|
||
$order_data = [];
|
||
if (is_array($order)) {
|
||
foreach ($order as $key => $data) {
|
||
$order_data[$key] = maybe_unserialize($data);
|
||
}
|
||
}
|
||
$admin_number = formipay_get_post_meta($form_id, 'whatsapp_admin');
|
||
$shortcodes = [
|
||
'{{product_name}}' => get_the_title($form_id),
|
||
'{{order_id}}' => $order_id,
|
||
'{{order_date}}' => $order['created_date'] ?? '',
|
||
'{{order_total}}' => $order['total'] ?? '',
|
||
'{{order_status}}' => $order['status'] ?? '',
|
||
'{{order_details}}' => $this->render_order_details($form_id, $order_id, maybe_unserialize($order['items'] ?? '')),
|
||
'{{payment_details}}' => $this->render_payment_details('', $form_id, $order_data, 'whatsapp'),
|
||
];
|
||
$message_template = wp_kses_post(formipay_get_post_meta($form_id, 'whatsapp_message'));
|
||
$message = strtr($message_template, $shortcodes);
|
||
$message = str_replace('%250A', '%0A', rawurlencode($message));
|
||
$whatsapp_url = 'https://api.whatsapp.com/send/?phone=' . urlencode($admin_number) . '&text=' . $message;
|
||
wp_safe_redirect($whatsapp_url);
|
||
exit;
|
||
}
|
||
}
|
||
}
|
||
|
||
public function handle_paypal_webhook(\WP_REST_Request $request) {
|
||
$body = $request->get_body();
|
||
$event = json_decode($body, true);
|
||
|
||
if (!$event || empty($event['event_type'])) {
|
||
return new \WP_Error('invalid_webhook', 'Invalid webhook payload');
|
||
}
|
||
|
||
// Example: get order_id from custom_id or invoice_number in resource
|
||
$resource = $event['resource'];
|
||
$order_id = null;
|
||
|
||
if (!empty($resource['custom_id'])) {
|
||
$order_id = intval($resource['custom_id']);
|
||
} elseif (!empty($resource['invoice_number'])) {
|
||
$order_id = intval($resource['invoice_number']);
|
||
}
|
||
|
||
if (!$order_id) {
|
||
return new \WP_Error('missing_order_id', 'Cannot find order ID in webhook');
|
||
}
|
||
|
||
// Map PayPal event to order status
|
||
switch ($event['event_type']) {
|
||
case 'PAYMENT.CAPTURE.COMPLETED':
|
||
$status = 'completed';
|
||
break;
|
||
case 'PAYMENT.CAPTURE.PENDING':
|
||
$status = 'pending';
|
||
break;
|
||
case 'PAYMENT.CAPTURE.REFUNDED':
|
||
case 'PAYMENT.CAPTURE.PARTIALLY_REFUNDED':
|
||
$status = 'refunded';
|
||
break;
|
||
case 'PAYMENT.CAPTURE.DENIED':
|
||
case 'PAYMENT.CAPTURE.REVERSED':
|
||
case 'PAYMENT.ORDER.CANCELLED':
|
||
$status = 'cancelled';
|
||
break;
|
||
default:
|
||
// Ignore unhandled events
|
||
return rest_ensure_response(['ignored' => true]);
|
||
}
|
||
|
||
// Update order and transaction
|
||
formipay_update_order_status([
|
||
'order_id' => $order_id,
|
||
'payment_gateway' => $this->gateway,
|
||
'status' => $status
|
||
]);
|
||
$this->update_transaction($order_id, $status, $resource);
|
||
|
||
return rest_ensure_response(['updated' => true, 'order_id' => $order_id, 'status' => $status]);
|
||
}
|
||
|
||
public function render_order_details($form_id, $order_id, $items) {
|
||
|
||
$order = formipay_get_order($order_id);
|
||
|
||
$target_length = 40;
|
||
$message_format = formipay_get_post_meta($form_id, 'whatsapp_message');
|
||
$order_details_message = '```';
|
||
if(!empty($items)){
|
||
foreach($items as $detail){
|
||
$qty = '';
|
||
if(isset($detail['qty']) && !empty($detail['qty'])){
|
||
$qty = $detail['qty'].'×';
|
||
}
|
||
$subtotal = formipay_price_format($detail['subtotal'], $form_id);
|
||
$qty_length = strlen($qty);
|
||
$subtotal_length = strlen($subtotal);
|
||
if(floatval($detail['subtotal']) < 0){
|
||
$subtotal_length = $subtotal_length + 1;
|
||
}
|
||
$dot_length = $target_length - ($qty_length + $subtotal_length);
|
||
$dots = str_repeat('.', intval($dot_length));
|
||
$order_details_message .= '%0A'.$detail['item'];
|
||
$order_details_message .= '%0A'.$qty.$dots.$subtotal;
|
||
}
|
||
$total = formipay_price_format($order['total'], $form_id);
|
||
$total_length = strlen($total);
|
||
$dot_length = $target_length - ($total_length + 6);
|
||
$dots = str_repeat('.', intval($dot_length));
|
||
$divider = str_repeat('_', intval($target_length));
|
||
$order_details_message .= '%0A'.$divider;
|
||
$order_details_message .= '%0A'.'';
|
||
$order_details_message .= '%0A'.'Total'.$dots.$total;
|
||
}
|
||
$order_details_message .= '%0A```';
|
||
|
||
$content = $order_details_message;
|
||
|
||
return $content;
|
||
|
||
}
|
||
|
||
public function render_payment_details($render, $form_id, $order_data, $submit_action_type) {
|
||
|
||
if($order_data['status'] == 'completed'){
|
||
return $render;
|
||
}
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
|
||
$get_trx = $this->get_transaction($order_data['id']);
|
||
if(!empty($get_trx)){
|
||
$meta = maybe_unserialize($get_trx->meta_data);
|
||
if(isset($meta['payer_action_url']) && !empty($meta['payer_action_url'])){
|
||
|
||
if($formipay_settings[$this->gateway.'_toggle'] !== false){
|
||
if($submit_action_type == 'thankyou'){
|
||
|
||
ob_start();
|
||
?>
|
||
<a id="upload-transfer-receipt" class="formipay-submit-button" href="<?php echo esc_url($meta['payer_action_url']) ?>">
|
||
<i class="bi bi-paypal"></i> <?php echo esc_html__( 'Pay Now', 'formipay' ); ?>
|
||
</a>
|
||
<?php
|
||
$render = ob_get_contents();
|
||
ob_get_clean();
|
||
|
||
}elseif($submit_action_type == 'whatsapp'){
|
||
|
||
$slug = 'thankyou';
|
||
if(!empty($formipay_settings['thankyou_link'])){
|
||
$slug = $formipay_settings['thankyou_link'];
|
||
}
|
||
|
||
// translators: %s is the Paypal payment URL.
|
||
$message = sprintf( __('> Payment Link: *%s*', 'formipay'), $meta['payer_action_url'] );
|
||
|
||
$render = $message;
|
||
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return $render;
|
||
|
||
}
|
||
|
||
public function render_email_action_button($button, $recipient, $order_data){
|
||
|
||
if(
|
||
$order_data['payment_gateway'] == $this->gateway &&
|
||
$order_data['status'] == 'on-hold' &&
|
||
$recipient !== 'admin'
|
||
){
|
||
|
||
$formipay_settings = get_option('formipay_settings');
|
||
$get_trx = $this->get_transaction($order_data['id']);
|
||
if(!empty($get_trx)){
|
||
$meta = maybe_unserialize($get_trx->meta_data);
|
||
if(isset($meta['payer_action_url']) && !empty($meta['payer_action_url'])){
|
||
|
||
$button = [
|
||
'url' => $meta['payer_action_url'],
|
||
'label' => __( 'Pay Now', 'formipay' )
|
||
];
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return $button;
|
||
|
||
}
|
||
|
||
}
|