first commit

This commit is contained in:
dwindown
2025-08-21 20:39:34 +07:00
commit 58c1497171
576 changed files with 177044 additions and 0 deletions

BIN
includes/.DS_Store vendored Normal file

Binary file not shown.

721
includes/Access.php Normal file
View File

@@ -0,0 +1,721 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Access {
use SingletonTrait;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'init', [$this, 'cpt'] );
add_action( 'admin_menu', [$this, 'add_submenu'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_admin'] );
add_filter( 'stm_wpcfto_boxes', [$this, 'cpt_post_fields_box'] );
add_filter( 'stm_wpcfto_fields', [$this, 'cpt_post_fields_content'] );
add_filter( 'formipay/product-config', [$this, 'product_config'], 50 );
add_filter( 'formipay/access-config', [$this, 'source_config'] );
add_filter( 'formipay/access-config', [$this, 'details_config'] );
// Admin Page
add_action( 'wp_ajax_formipay_access_items_get_products', [$this, 'formipay_access_items_get_products'] );
add_action( 'wp_ajax_formipay-tabledata-access-items', [$this, 'formipay_tabledata_access_items'] );
add_action( 'wp_ajax_formipay-create-access-item-post', [$this, 'formipay_create_access_item_post'] );
add_action( 'wp_ajax_formipay-delete-access-item', [$this, 'formipay_delete_access_item'] );
add_action( 'wp_ajax_formipay-bulk-delete-access-item', [$this, 'formipay_bulk_delete_access_item'] );
add_action( 'wp_ajax_formipay-duplicate-access-item', [$this, 'formipay_duplicate_access_item'] );
}
public function cpt() {
$labels = array(
'name' => __('Access Items', 'formipay'),
'singular_name' => __('Access Item', 'formipay'),
'add_new' => __('Add New', 'formipay'),
'add_new_item' => __('Add New Access Item', 'formipay'),
'edit' => __('Edit', 'formipay'),
'edit_item' => __('Edit Access Item', 'formipay'),
'new_item' => __('New Access Item', 'formipay'),
'view' => __('View', 'formipay'),
'view_item' => __('View Access Item', 'formipay'),
'search_items' => __('Search Access Item', 'formipay'),
'not_found' => __('No access items found', 'formipay'),
'not_found_in_trash' => __('No access items found in trash', 'formipay'),
'parent' => __('Access Item Parent', 'formipay')
);
$args = array(
'label' => 'Formipay Access',
'description' => __('The items your buyer will get after purchase.', 'formipay'),
'labels' => $labels,
'public' => false,
'supports' => array( 'title' ),
'hierarchical' => false,
'has_archive' => false,
'show_ui' => true,
'show_in_menu' => false,
'show_in_rest' => false,
'query_var' => true
);
register_post_type( 'formipay-access', $args );
}
public function add_submenu() {
add_submenu_page(
'formipay',
__( 'Access Items', 'formipay' ),
__( 'Access Items', 'formipay' ),
'manage_options',
'formipay-access-items',
[$this, 'formipay_access_items']
);
}
public function formipay_access_items() {
include_once FORMIPAY_PATH . 'admin/page-access-items.php';
}
public function enqueue_admin() {
global $current_screen;
$screen = get_current_screen();
// Check that we are on the 'Checker' post editor screen
if ( $screen->post_type === 'formipay-access' && $screen->base === 'post' ) {
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION );
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
wp_localize_script( 'sweetalert2', 'formipay_admin', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
] );
}
if($current_screen->id == 'formipay_page_formipay-access-items') {
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], 'all');
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.mermaid.min.css', [], '6.2.0', 'all' );
wp_enqueue_style( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'page-access-items', FORMIPAY_URL . 'admin/assets/css/page-access-items.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.5', true);
wp_enqueue_script( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/js/admin-pages.js', ['jquery'], FORMIPAY_VERSION, true );
wp_enqueue_script( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.production.min.js', ['jquery'], '6.2.0', true );
wp_enqueue_script( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.js', [], FORMIPAY_VERSION, true );
wp_enqueue_script( 'page-access-items', FORMIPAY_URL . 'admin/assets/js/page-access-items.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
wp_localize_script( 'page-access-items', 'formipay_access_page', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
'columns' => [
'id' => esc_html__( 'ID', 'formipay' ),
'title' => esc_html__( 'Title', 'formipay' ),
'type' => esc_html__( 'Type', 'formipay' ),
'products' => esc_html__( 'Product Relation', 'formipay' ),
'status' => esc_html__( 'Status', 'formipay' ),
],
'filter_form' => [
'products' => [
'placeholder' => esc_html__( 'Filter by Product', 'formipay' ),
'noresult_text' => esc_html__( 'No results found', 'formipay' )
]
],
'modal' => [
'add' => [
'title' => esc_html__( 'Your New Item Title', 'formipay' ),
'validation' => esc_html__( 'Item\'s title is still empty. Please input the title before continue', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Create New Item', 'formipay' )
],
'delete' => [
'question' => esc_html__( 'Do you want to delete the item?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Delete Permanently', 'formipay' )
],
'bulk_delete' => [
'question' => esc_html__( 'Do you want to delete the selected the form(s)?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
'duplicate' => [
'question' => esc_html__( 'Do you want to duplicate the item?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
],
'nonce' => wp_create_nonce('formipay-admin-access-nonce')
] );
}
}
public function cpt_post_fields_box($boxes) {
$boxes['formipay_access_settings'] = array(
'post_type' => array('formipay-access'),
'label' => __('Details', 'formipay'),
);
return $boxes;
}
public function cpt_post_fields_content($fields) {
$fields['formipay_access_settings'] = array();
$fields = apply_filters( 'formipay/access-config', $fields );
return $fields;
}
public function product_config($fields){
// Product Access Group
$product_access_group = array(
'setting_product_access' => array(
'type' => 'group_title',
'label' => __( 'Digital Accesses', 'formipay' ),
'description' => __( 'List files & URLs that buyer can get after complete the transaction', 'formipay' ),
'group' => 'started'
),
'product_accesses' => array(
'type' => 'autocomplete',
'label' => __( 'Accesses', 'formipay' ),
'post_type' => array('formipay-access'),
'limit' => 10
),
'product_access_to_email' => array(
'type' => 'checkbox',
'label' => __( 'Send Access Magic Link to Email', 'formipay' ),
)
);
$product_access_group = apply_filters( 'formipay/product-settings/tab:access/group:product-access', $product_access_group );
$last_product_access_group = array_key_last($product_access_group);
$product_access_group[$last_product_access_group]['group'] = 'ended';
$product_access_group = apply_filters( 'formipay/product-settings/tab:access', $product_access_group );
$fields['formipay_product_settings']['access'] = array(
'name' => __( 'Access', 'formipay' ),
'fields' => $product_access_group
);
return $fields;
}
public function source_config($fields) {
$get_attachments = get_posts([
'post_type' => 'attachment',
'posts_per_page' => -1
]);
$attachments = [];
if(!empty($get_attachments)){
foreach($get_attachments as $attachment){
$attachments[$attachment->ID] = get_the_title($attachment->ID) . ' (' . $attachment->post_mime_type . ')';
}
}
// Group : Fields
$field_group = array(
'file_url_group' => array(
'type' => 'group_title',
'label' => __( 'Source', 'formipay' ),
'description' => __( 'Define the source of this access.', 'formipay' ),
'group' => 'started'
),
'access_type' => array(
'type' => 'select',
'label' => __( 'Type', 'formipay' ),
'options' => array(
'document' => __( 'Document', 'formipay' ),
'redirect' => __( 'Redirect URL', 'formipay' ),
'download' => __( 'File', 'formipay' )
),
'value' => 'document'
),
'access_document' => array(
'type' => 'tinymce',
'label' => __( 'Document', 'formipay' ),
'dependency' => array(
'key' => 'access_type',
'value' => 'document'
),
),
'access_redirect_url' => array(
'type' => 'text',
'label' => __( 'URL', 'formipay' ),
'dependency' => array(
'key' => 'access_type',
'value' => 'redirect'
),
'value' => 'https://'
),
'access_download_source' => array(
'type' => 'select',
'label' => __( 'File Source', 'formipay' ),
'options' => array(
'wp_media' => __( 'WP Media', 'formipay' ),
'external' => __( 'External', 'formipay' )
),
'dependency' => array(
'key' => 'access_type',
'value' => 'download'
),
'value' => 'wp_media'
),
'access_attachment' => array(
'type' => 'select',
'label' => __( 'Select File', 'formipay' ),
'options' => $attachments,
'dependency' => array(
array(
'key' => 'access_type',
'value' => 'download'
),
array(
'key' => 'access_download_source',
'value' => 'wp_media'
)
),
'dependencies' => '&&'
),
'access_url' => array(
'type' => 'text',
'label' => __( 'File URL', 'formipay' ),
'dependency' => array(
array(
'key' => 'access_type',
'value' => 'download'
),
array(
'key' => 'access_download_source',
'value' => 'external'
)
),
'dependencies' => '&&',
'value' => 'https://'
)
);
$field_group = apply_filters( 'formipay/access-settings/tab:source/group:source', $field_group );
$last_field_group = array_key_last($field_group);
$field_group[$last_field_group]['group'] = 'ended';
$button_group = array(
'button_group' => array(
'type' => 'group_title',
'label' => __( 'Action Button', 'formipay' ),
'description' => __( 'Set your action button for this access', 'formipay' ),
'group' => 'started'
),
'button_text' => array(
'type' => 'text',
'label' => __( 'Button Text', 'formipay' ),
'value' => __( 'Download', 'formipay' ),
)
);
$button_group = apply_filters( 'formipay/access-settings/tab:source/group:source', $button_group );
$last_button_group = array_key_last($button_group);
$button_group[$last_button_group]['group'] = 'ended';
$all_group_merged = array_merge($field_group, $button_group);
$settings_all_fields = apply_filters( 'formipay/access-settings/tab:source', $all_group_merged );
$fields['formipay_access_settings']['source'] = array(
'name' => __('Source', 'formipay'),
'fields' => $settings_all_fields
);
return $fields;
}
public function details_config($fields) {
// Group : Fields
$field_group = array(
'details_access_group' => array(
'type' => 'group_title',
'label' => __( 'Details', 'formipay' ),
'group' => 'started'
),
'details_icon' => array(
'type' => 'image',
'label' => __( 'Custom Thumbnail', 'formipay' ),
'description' => __( 'Please note this will be displayed in approx. 36x36 pixels', 'formipay' ),
),
'details_filetype' => array(
'type' => 'text',
'label' => __( 'File Type', 'formipay' ),
'description' => __( 'Example: zip for yourfile.zip.', 'formipay' ),
'dependency' => array(
array(
'key' => 'access_type',
'value' => 'download',
'section' => 'source'
),
array(
'key' => 'access_download_source',
'value' => 'external',
'section' => 'source'
),
),
'dependencies' => '&&'
),
'details_filesize' => array(
'type' => 'text',
'label' => __('File Size', 'formipay'),
'dependency' => array(
array(
'key' => 'access_type',
'value' => 'download',
'section' => 'source'
),
array(
'key' => 'access_download_source',
'value' => 'external',
'section' => 'source'
),
),
'dependencies' => '&&'
),
'details_short_description' => array(
'type' => 'text',
'label' => __( 'Short Description', 'formipay' ),
'description' => __( 'You may put some important note to your buyer about this access', 'formipay' )
)
);
$field_group = apply_filters( 'formipay/access-settings/tab:details/group:file-information', $field_group );
$last_field_group = array_key_last($field_group);
$field_group[$last_field_group]['group'] = 'ended';
$details_all_fields = apply_filters( 'formipay/access-settings/tab:details', $field_group );
$fields['formipay_access_settings']['details'] = array(
'name' => __('Details', 'formipay'),
'fields' => $details_all_fields
);
return $fields;
}
public function formipay_tabledata_access_items() {
check_ajax_referer( 'formipay-admin-access-nonce', '_wpnonce' );
$args = [
'post_type' => 'formipay-access',
'posts_per_page' => -1,
'post_status' => array( 'pending', 'draft', 'publish' )
];
if(!empty($_REQUEST['post_status']) && 'all' !== $_REQUEST['post_status']){
$args['post_status'] = [ sanitize_text_field( wp_unslash( $_REQUEST['post_status'] ) ) ];
}
if(!empty($_REQUEST['product'])){
$product_id = intval($_REQUEST['product']);
$access_items_ids = formipay_get_post_meta($product_id, 'product_accesses');
$access_items_ids = explode(',', $access_items_ids);
$args['post__in'] = $access_items_ids;
}
$get_total_access_items = get_posts($args);
if(isset($_REQUEST['limit'])){
$args['posts_per_page'] = intval($_REQUEST['limit']);
}
if(isset($_REQUEST['offset'])){
$args['offset'] = intval($_REQUEST['offset']);
}
if(!empty($_REQUEST['search'])){
$args['s'] = sanitize_text_field( wp_unslash($_REQUEST['search']) );
}
if(!empty($_REQUEST['sort']) && !empty($_REQUEST['orderby'])){
$args['order'] = sanitize_text_field( wp_unslash($_REQUEST['sort']) );
$args['orderby'] = sanitize_text_field( wp_unslash($_REQUEST['orderby']) );
}
$get_access_items = get_posts($args);
// All Forms
$forms = get_posts([
'post_type' => 'formipay-form',
'posts_per_page' => -1,
]);
$access_items = [];
if(!empty($get_access_items)){
foreach($get_access_items as $access){
$product_titles = [];
if(!empty($forms)){
foreach($forms as $form){
$form_id = $form->ID;
$accesses = explode(',', formipay_get_post_meta($form_id, 'product_accesses'));
if(in_array($access->ID, $accesses)){
$product_titles[] = get_the_title($form_id);
}
}
}
$access_items[] = [
'ID' => $access->ID,
'title' => get_the_title($access->ID),
'type' => ucfirst(formipay_get_post_meta($access->ID, 'access_type')),
'products' => $product_titles,
'status' => $access->post_status
];
}
}
// Get status counts using WordPress API
$counts = wp_count_posts('formipay-access');
$status_counts = [];
foreach ($counts as $status => $count) {
$status_counts[$status] = $count;
}
$status_counts['all'] = array_sum($status_counts);
$response = [
'results' => $access_items,
'total' => count($get_total_access_items), // Total number of forms
'posts_report' => array_map('intval', (array)$status_counts)
];
wp_send_json($response);
}
public function formipay_access_items_get_products() {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if (!isset($_POST['search']) || empty($_POST['search'])) {
wp_send_json_error( __('No search term provided.', 'formipay') );
}
$search_term = sanitize_text_field( wp_unslash( $_POST['search'] ));
$query = get_posts([
'post_type' => 'formipay-form',
's' => $search_term,
'posts_per_page' => -1,
]);
$results = [];
if(!empty($query)){
foreach($query as $post){
$results[] = [
'value' => $post->ID,
'label' => $post->post_title,
];
}
}
wp_send_json($results);
}
public function formipay_create_access_item_post() {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if( !empty($_REQUEST['title']) ){
$title = sanitize_text_field( wp_unslash($_REQUEST['title']) );
$post_id = wp_insert_post( [
'post_title' => $title,
'post_type' => 'formipay-access',
], true );
if(is_wp_error($post_id)){
wp_send_json_error( [
'message' => $post_id->get_error_message()
] );
}
wp_send_json_success( [
'edit_post_url' => admin_url('post.php?post='.$post_id.'&action=edit')
] );
}
wp_send_json_error( [
'message' => esc_html__( 'Item\'s title is empty.', 'formipay' )
] );
}
public function formipay_delete_access_item() {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if( empty($_REQUEST['id']) ){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'Failed to delete item. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$post_id = intval($_REQUEST['id']);
$delete = wp_delete_post($post_id, true);
if(is_wp_error( $delete )){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'Failed to delete item. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
wp_send_json_success( [
'title' => esc_html__( 'Deleted', 'formipay' ),
'message' => esc_html__( 'Item is deleted permanently.', 'formipay' ),
'icon' => 'success'
] );
}
public function formipay_bulk_delete_access_item() {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if( empty($_REQUEST['ids']) ){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'There is no access item selected. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$ids = isset($_REQUEST['ids']) ? $_REQUEST['ids'] : [];
$success = 0;
$failed = 0;
$report = __( 'Done.', 'formipay' );
if(!empty($ids)){
foreach($ids as $id){
$delete = wp_delete_post($id, true);
if(is_wp_error( $delete )){
$failed++;
}else{
$success++;
}
}
}
if($success > 0){
$report .= sprintf( __( ' Deleted %d item(s).', 'formipay' ), $success);
}
if($failed > 0){
$report .= sprintf( __( ' Failed %d item(s).', 'formipay' ), $failed);
}
wp_send_json_success( [
'title' => esc_html__( 'Done!', 'formipay' ),
'message' => $report,
'icon' => 'info'
] );
}
public function formipay_duplicate_access_item() {
check_ajax_referer( 'formipay-admin-access-nonce', 'nonce' );
if( empty($_REQUEST['id']) ){
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Item is not defined.', 'formipay' ),
'icon' => 'error'
] );
}
$post_id = intval($_REQUEST['id']);
$post = get_post($post_id);
if (!$post) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Item is not defined.', 'formipay' ),
'icon' => 'error'
] );
}
// Pastikan hanya tipe posting yang bisa diduplikasi
if (!in_array($post->post_type, ['formipay-access'])) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Wrong Post Type.', 'formipay' ),
'icon' => 'error'
] );
}
// Buat duplikat post
$duplicate_post = [
'post_title' => $post->post_title . ' (Copy)',
'post_content' => $post->post_content,
'post_status' => 'draft', // Set sebagai draft
'post_type' => $post->post_type,
'post_author' => $post->post_author,
];
// Masukkan duplikat ke database
$new_post_id = wp_insert_post($duplicate_post);
if (is_wp_error($new_post_id)) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Something happened. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
// Salin semua meta data
$meta_data = get_post_meta($post_id);
foreach ($meta_data as $key => $values) {
foreach ($values as $value) {
update_post_meta($new_post_id, $key, maybe_unserialize($value));
}
}
wp_send_json_success( [
'title' => esc_html__('Duplicated!', 'formipay'),
'message' => esc_html__( 'Form is successfully duplicated.', 'formipay' ),
'icon' => 'success'
] );
}
}

768
includes/Coupon.php Normal file
View File

@@ -0,0 +1,768 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Coupon {
use SingletonTrait;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'init', [$this, 'cpt'] );
add_action( 'admin_menu', [$this, 'add_submenu'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_admin'] );
add_filter( 'stm_wpcfto_boxes', [$this, 'cpt_post_fields_box'] );
add_filter( 'stm_wpcfto_fields', [$this, 'cpt_post_fields_content'] );
add_filter( 'formipay/coupon-config', [$this, 'rule_config'] );
add_filter( 'formipay/coupon-config', [$this, 'restriction_config'] );
add_filter( 'formipay/admin-editor/layout/static-elements-sort', [$this, 'add_static_element'] );
add_action( 'formipay/render/static-element', [$this, 'render_static_element'] );
add_action( 'wp_ajax_check_coupon_code', [$this, 'check_coupon_code'] );
add_action( 'wp_ajax_nopriv_check_coupon_code', [$this, 'check_coupon_code'] );
// Admin Page
add_action( 'wp_ajax_formipay_coupon_get_products', [$this, 'formipay_coupon_get_products'] );
add_action( 'wp_ajax_formipay-tabledata-coupons', [$this, 'formipay_tabledata_coupons'] );
add_action( 'wp_ajax_formipay-create-coupon-post', [$this, 'formipay_create_coupon_post'] );
add_action( 'wp_ajax_formipay-delete-coupon', [$this, 'formipay_delete_coupon'] );
add_action( 'wp_ajax_formipay-bulk-delete-coupon', [$this, 'formipay_bulk_delete_coupon'] );
add_action( 'wp_ajax_formipay-duplicate-coupon', [$this, 'formipay_duplicate_coupon'] );
// Order
add_filter( 'formipay/order/order-details', [$this, 'order_details'], 99, 3 );
// Paypal
add_filter( 'formipay/order/paypal/payload/breakdown', [$this, 'add_discount_item'], 99, 3 );
}
public function cpt() {
$labels = array(
'name' => __('Coupons', 'formipay'),
'singular_name' => __('Coupon', 'formipay'),
// 'menu_name' => 'Formipay',
'add_new' => __('Add New', 'formipay'),
'add_new_item' => __('Add New Coupon', 'formipay'),
'edit' => __('Edit', 'formipay'),
'edit_item' => __('Edit Coupon', 'formipay'),
'new_item' => __('New Coupon', 'formipay'),
'view' => __('View', 'formipay'),
'view_item' => __('View Coupon', 'formipay'),
'search_items' => __('Search Coupon', 'formipay'),
'not_found' => __('No coupons found', 'formipay'),
'not_found_in_trash' => __('No coupons found in trash', 'formipay'),
'parent' => __('Coupon Parent', 'formipay')
);
$args = array(
'label' => 'Coupons',
'description' => __('Coupon for marketing campaign', 'formipay'),
'labels' => $labels,
'public' => false,
'supports' => array( 'title' ),
'hierarchical' => false,
'has_archive' => false,
'show_ui' => true,
'show_in_menu' => false,
'show_in_rest' => false,
'query_var' => true
);
register_post_type( 'formipay-coupon', $args );
}
public function add_submenu() {
add_submenu_page(
'formipay',
__( 'Coupons', 'formipay' ),
__( 'Coupons', 'formipay' ),
'manage_options',
'formipay-coupons',
[$this, 'formipay_coupon'],
);
}
public function formipay_coupon() {
include_once FORMIPAY_PATH . 'admin/page-coupons.php';
}
public function enqueue_admin() {
global $current_screen;
if($current_screen->id == 'formipay_page_formipay-coupons') {
wp_enqueue_style( 'page-coupons', FORMIPAY_URL . 'admin/assets/css/page-coupons.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'page-coupons', FORMIPAY_URL . 'admin/assets/js/page-coupons.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
wp_localize_script( 'page-coupons', 'formipay_coupons_page', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
'columns' => [
'id' => esc_html__( 'ID', 'formipay' ),
'code' => esc_html__( 'Coupon Code', 'formipay' ),
'products' => esc_html__( 'Product Relation', 'formipay' ),
'usages' => esc_html__( 'Usages', 'formipay' ),
'date_limit' => esc_html__( 'Date Limit', 'formipay' ),
'status' => esc_html__( 'Status', 'formipay' ),
'type' => esc_html__( 'Type', 'formipay' ),
'amount' => esc_html__( 'Amount', 'formipay' )
],
'filter_form' => [
'products' => [
'placeholder' => esc_html__( 'Filter by Product', 'formipay' ),
'noresult_text' => esc_html__( 'No results found', 'formipay' )
]
],
'modal' => [
'add' => [
'title' => esc_html__( 'Your New Coupon Code', 'formipay' ),
'validation' => esc_html__( 'Coupon code is still empty. Please input the code before continue', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Create New Coupon', 'formipay' )
],
'delete' => [
'question' => esc_html__( 'Do you want to delete the coupon?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Delete Permanently', 'formipay' )
],
'bulk_delete' => [
'question' => esc_html__( 'Do you want to delete the selected the coupon(s)?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
'duplicate' => [
'question' => esc_html__( 'Do you want to duplicate the coupon?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
],
'nonce' => wp_create_nonce('formipay-admin-coupon-page')
] );
}
$screen = get_current_screen();
if ( $screen->post_type === 'formipay-coupon' && $screen->base === 'post' ) {
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
wp_localize_script( 'sweetalert2', 'formipay_admin', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
] );
}
}
public function cpt_post_fields_box($boxes) {
$boxes['formipay_coupon_settings'] = array(
'post_type' => array('formipay-coupon'),
'label' => __('Details', 'formipay'),
);
return $boxes;
}
public function cpt_post_fields_content($fields) {
$fields['formipay_coupon_settings'] = array();
$fields = apply_filters( 'formipay/coupon-config', $fields );
return $fields;
}
public function rule_config($fields) {
// Group : Rules
$rules_group = array(
'active' => array(
'type' => 'checkbox',
'label' => __( 'Active this coupon', 'formipay' ),
'value' => true
),
'rules_group' => array(
'type' => 'group_title',
'label' => __( 'Rules', 'formipay' ),
'description' => __( 'Define the rules of this coupon.', 'formipay' ),
'group' => 'started'
),
'case_sensitive' => array(
'type' => 'checkbox',
'label' => __( 'Case Sensitive', 'formipay' ),
'description' => __( 'If this activated, coupon codes must be entered with the exact capitalization as specified. <br>
For example code <B>SAVE10</b>:<br>
- <b>SAVE10</b> is valid<br>
- <b>save10</b> is invalid<br>
- <b>Save10</b> is invalid.', 'formipay' )
),
'type' => array(
'type' => 'radio',
'label' => __( 'Type', 'formipay' ),
'options' => array(
'fixed' => __( 'Fixed', 'formipay' ),
'percentage' => __( 'Percentage', 'formipay' )
)
),
'amount' => array(
'type' => 'number',
'label' => __( 'Amount', 'formipay' ),
'description' => sprintf('<span style="color: red;">' . __( 'Be carefully, this amount is not regarding the currency.', 'formipay') . '</span>' )
),
'free_shipping' => array(
'type' => 'checkbox',
'label' => __( 'Free Shipping', 'formipay' ),
'description' => __( 'Shipping cost will be free when this coupon applied.', 'formipay' )
),
'quantity_active' => array(
'type' => 'checkbox',
'label' => __( 'Influenced by Quantity', 'formipay' ),
'description' => __( 'Example: when buyer buy 4 pcs of item, 4 × discount amount will be implemented.', 'formipay' ),
'dependency' => array(
'key' => 'type',
'value' => 'fixed'
)
),
'max_discount' => array(
'type' => 'number',
'label' => __( 'Max Discount Amount', 'formipay' ),
'description' => __( 'Leave it empty to not limit the max discount amount.', 'formipay' )
),
);
$rules_group = apply_filters( 'formipay/coupon-settings/tab:rules/group:rules', $rules_group );
$last_rules_group = array_key_last($rules_group);
$rules_group[$last_rules_group]['group'] = 'ended';
$all_group_merged = $rules_group;
$settings_all_fields = apply_filters( 'formipay/coupon-settings/tab:rules', $all_group_merged );
$settings_fields = [];
if(!empty($settings_all_fields)){
foreach($settings_all_fields as $key => $value){
$settings_fields[$key] = $value;
}
}
$fields['formipay_coupon_settings']['rules'] = array(
'name' => __('Rules', 'formipay'),
'fields' => $settings_fields
);
return $fields;
}
public function restriction_config($fields) {
// Group : Restrictions
$restriction_group = array(
'restriction_group' => array(
'type' => 'group_title',
'label' => __( 'Restrictions', 'formipay' ),
'group' => 'started'
),
'use_limit' => array(
'type' => 'number',
'label' => __( 'Usage Limit', 'formipay' ),
'description' => __( 'Leave it empty or 0 (zero) to set it as unlimited usage.', 'formipay' )
),
'date_limit' => array(
'type' => 'date',
'label' => __( 'Date Limit', 'formipay' ),
'description' => __( 'Chosen date is the last day the coupon can be used. Leave it empty tio set it as no-limit date.', 'formipay' )
),
'forms' => array(
'type' => 'autocomplete',
'post_type' => array('formipay-form'),
'label' => __( 'These forms can use the coupon', 'formipay' ),
'description' => __( 'Leave it empty to enable all forms to use this coupon. <span style="color: red;">Please be carefully, this coupon amount is not regarding the currency of the selected form.</span>', 'formipay' )
)
);
$restriction_group = apply_filters( 'formipay/coupon-settings/tab:restriction/group:restrictions', $restriction_group );
$last_restriction_group = array_key_last($restriction_group);
$restriction_group[$last_restriction_group]['group'] = 'ended';
$details_all_fields = apply_filters( 'formipay/coupon-settings/tab:restriction', $restriction_group );
$details_fields = [];
if(!empty($details_all_fields)){
foreach($details_all_fields as $key => $value){
$details_fields[$key] = $value;
}
}
$fields['formipay_coupon_settings']['restriction'] = array(
'name' => __('Restrictions', 'formipay'),
'fields' => $details_fields
);
return $fields;
}
public function add_static_element($elements) {
$elements[] = [
'id' => 'coupon_field',
'label' => __( 'Coupon Field', 'formipay' )
];
return $elements;
}
public function render_static_element($element_id) {
if($element_id == 'coupon_field'){
?>
<div class="formipay-field-group">
<div class="formipay-coupon-field-group">
<input type="text" name="coupon_code"
class="formipay-input formipay-code-input"
value="<?php echo
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
isset($_GET['coupon']) ? esc_attr( sanitize_text_field( wp_unslash($_GET['coupon']) ) ) : ''
?>" placeholder="<?php echo esc_attr__('Your coupon code', 'formipay'); ?>"
data-label="<?php echo esc_attr__('Coupon Code', 'formipay'); ?>">
<button type="button" data-text="<?php echo esc_attr__( 'Apply', 'formipay' ); ?>" data-checking="<?php echo esc_attr__( 'Checking...', 'formipay' ); ?>" id="apply_coupon_code"><?php echo esc_attr__( 'Apply', 'formipay' ); ?></button>
</div>
<div class="formipay-coupon-alert-message"></div>
</div>
<?php
}
}
public function check_coupon_code() {
check_ajax_referer( 'formipay-frontend-nonce', 'security' );
$code = isset($_REQUEST['code']) ? sanitize_text_field( wp_unslash($_REQUEST['code']) ) : '';
$form_id = isset($_REQUEST['form']) ? intval( $_REQUEST['form'] ) : 0;
$check_code = formipay_get_coupon_id_by_code($code, $form_id);
if(false == $check_code){
wp_send_json_error( [
'message' => __( 'Coupon code is not valid', 'formipay' )
] );
}
wp_send_json_success();
}
public function order_details($details, $form_id, $order_data) {
if( isset($order_data['coupon_code']) && !empty($order_data['coupon_code']) ) {
$coupon_id = formipay_get_coupon_id_by_code( sanitize_text_field( $order_data['coupon_code'] ), $form_id );
if(false !== $coupon_id) {
$amount = floatval( formipay_get_post_meta( $coupon_id, 'amount' ) );
$discount = $amount;
$coupon_type = formipay_get_post_meta( $coupon_id, 'type' );
$qty = 1;
if( formipay_get_post_meta( $form_id, 'product_quantity_toggle' ) == 'on' ){
$qty = intval($order_data['qty']);
}
if( $coupon_type == 'percentage' ){
$product_price = floatval( formipay_get_post_meta( $form_id, 'product_price' ) );
$amount = $amount / 100;
$discount = $product_price * $amount;
}
$amount = $discount * $qty;
$amount = $amount * -1;
$details[] = [
'item' => esc_html( $order_data['coupon_code'] ),
'amount' => floatval($amount),
'subtotal' => floatval($amount),
'context' => 'sub'
];
}
}
return $details;
}
public function count_order_by_coupon_code($coupon_code) {
global $wpdb;
$table = $wpdb->prefix .'formipay_orders';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$data = $wpdb->get_results(
$wpdb->prepare(
"SELECT COUNT(id) as count FROM %i WHERE `items` LIKE %s", $table, '%' . $wpdb->esc_like($coupon_code) . '%'
)
);
return $data[0]->count;
}
public function add_discount_item($breakdown, $currency, $order_data) {
if( isset($order_data['form_data']['coupon_code']) && !empty($order_data['form_data']['coupon_code']) ) {
$form_id = $order_data['form_id'];
$coupon_id = formipay_get_coupon_id_by_code( sanitize_text_field( $order_data['form_data']['coupon_code'] ), $form_id );
if(false !== $coupon_id) {
$amount = floatval( formipay_get_post_meta( $coupon_id, 'amount' ) );
$discount = $amount;
$coupon_type = formipay_get_post_meta( $coupon_id, 'type' );
$qty = 1;
if( formipay_get_post_meta( $form_id, 'product_quantity_toggle' ) == 'on' ){
$qty = intval($order_data['form_data']['qty']);
}
if( $coupon_type == 'percentage' ){
$product_price = floatval( formipay_get_post_meta( $form_id, 'product_price' ) );
$amount = $amount / 100;
$discount = $product_price * $amount;
}
$breakdown['item_total']['value'] += $discount;
$breakdown['discount'] = array(
"currency_code" => $currency,
"value" => $discount
);
}
}
return $breakdown;
}
public function formipay_tabledata_coupons() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
$args = [
'post_type' => 'formipay-coupon',
'posts_per_page' => -1,
'post_status' => array( 'pending', 'draft', 'publish' )
];
$get_total_coupons = get_posts($args);
$coupon_status = [
'all' => 0,
'active' => 0,
'inactive' => 0
];
if(!empty($get_total_coupons)){
foreach($get_total_coupons as $coupon){
$status = (formipay_get_post_meta($coupon->ID, 'active') == 'on') ? 'active' : 'inactive';
$coupon_status['all'] = intval($coupon_status['all']) + 1;
if($status == 'active'){
$coupon_status['active'] = intval($coupon_status['active']) + 1;
}elseif($status == 'inactive'){
$coupon_status['inactive'] = intval($coupon_status['inactive']) + 1;
}
}
}
if(isset($_REQUEST['limit'])){
$args['posts_per_page'] = intval($_REQUEST['limit']);
}
if(isset($_REQUEST['offset'])){
$args['offset'] = intval($_REQUEST['offset']);
}
if(!empty($_REQUEST['search'])){
$args['s'] = sanitize_text_field( wp_unslash($_REQUEST['search']) );
}
$get_coupons = get_posts($args);
$coupons = [];
if(!empty($get_coupons)){
foreach($get_coupons as $key => $coupon){
$active = true;
$products = !empty(formipay_get_post_meta($coupon->ID, 'forms')) ? explode(',', formipay_get_post_meta($coupon->ID, 'forms')) : [];
$product_titles = [];
if(!empty($products)){
foreach($products as $product_id){
if ( FALSE !== get_post_status( $product_id ) ) {
$product_titles[] = [
'flag' => formipay_get_flag_by_currency(formipay_get_post_meta($product_id, 'product_currency')),
'currency' => formipay_get_post_meta($product_id, 'product_currency'),
'title' => get_the_title($product_id)
];
}
}
}
if(!empty($_REQUEST['product'])){
$product_id = intval($_REQUEST['product']);
$forms = formipay_get_post_meta($coupon->ID, 'forms');
if(false !== $forms){
$forms = explode(',', $forms);
$found = false;
if(!empty($forms)){
foreach($forms as $form_id){
if($product_id == $form_id){
$active = true;
$found = true;
break;
}
$active = false;
}
if(false == $found){
unset($get_total_coupons[$key]);
}
}
}
}
$date_limit = formipay_get_post_meta($coupon->ID, 'date_limit');
if($active){
$coupons[] = [
'ID' => $coupon->ID,
'code' => get_the_title($coupon->ID),
'products' => $product_titles,
'value' => floatval(formipay_get_post_meta($coupon->ID, 'amount')),
'type' => ucfirst(formipay_get_post_meta($coupon->ID, 'type')),
'case_sensitive' => formipay_get_post_meta($coupon->ID, 'case_sensitive'),
'usages' => [
'used' => $this->count_order_by_coupon_code(get_the_title($coupon->ID)),
'limit' => (intval(formipay_get_post_meta($coupon->ID, 'use_limit')) > 0) ? formipay_get_post_meta($coupon->ID, 'use_limit') : '∞'
],
'date_limit' => false !== $date_limit ? formipay_date('Y-m-d', intval(formipay_get_post_meta($coupon->ID, 'date_limit')) / 1000) : 'none',
'status' => (formipay_get_post_meta($coupon->ID, 'active') == 'on') ? 'Active' : 'Inactive'
];
}
}
}
$response = [
'results' => $coupons,
'total' => count($get_total_coupons), // Total number of forms
'posts_report' => array_map('intval', (array)$coupon_status)
];
wp_send_json($response);
}
public function formipay_coupon_get_products() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
if (!isset($_POST['search']) || empty($_POST['search'])) {
wp_send_json_error( __('No search term provided.', 'formipay') );
}
$search_term = sanitize_text_field( wp_unslash($_POST['search']) );
$query = get_posts([
'post_type' => 'formipay-form',
's' => $search_term,
'posts_per_page' => -1,
]);
$results = [];
if(!empty($query)){
foreach($query as $post){
$results[] = [
'value' => $post->ID,
'label' => $post->post_title,
];
}
}
wp_send_json($results);
}
public function formipay_create_coupon_post() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
$code = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : '';
if( !empty($code) && '' !== $code ){
$post_id = wp_insert_post( [
'post_title' => $code,
'post_type' => 'formipay-coupon',
], true );
if(is_wp_error($post_id)){
wp_send_json_error( [
'message' => $post_id->get_error_message()
] );
}
update_post_meta($post_id, 'type', 'percentage');
update_post_meta($post_id, 'amount', 0);
wp_send_json_success( [
'edit_post_url' => admin_url('post.php?post='.$post_id.'&action=edit')
] );
}
wp_send_json_error( [
'message' => esc_html__( 'Coupon code is empty.', 'formipay' )
] );
}
public function formipay_delete_coupon() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
$post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$delete = wp_delete_post($post_id, true);
if(is_wp_error( $delete )){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'Failed to delete coupon. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
wp_send_json_success( [
'title' => esc_html__( 'Deleted', 'formipay' ),
'message' => esc_html__( 'Coupon is deleted permanently.', 'formipay' ),
'icon' => 'success'
] );
}
public function formipay_bulk_delete_coupon() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
if( empty($_REQUEST['ids']) ){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'There is no coupon selected. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$ids = isset($_REQUEST['ids']) ? $_REQUEST['ids'] : [];
$success = 0;
$failed = 0;
$report = __( 'Done.', 'formipay' );
if(!empty($ids)){
foreach($ids as $id){
$delete = wp_delete_post($id, true);
if(is_wp_error( $delete )){
$failed++;
}else{
$success++;
}
}
}
if($success > 0){
$report .= sprintf( __( ' Deleted %d coupon(s).', 'formipay' ), $success);
}
if($failed > 0){
$report .= sprintf( __( ' Failed %d coupon(s).', 'formipay' ), $failed);
}
wp_send_json_success( [
'title' => esc_html__( 'Done!', 'formipay' ),
'message' => $report,
'icon' => 'info'
] );
}
public function formipay_duplicate_coupon() {
check_ajax_referer( 'formipay-admin-coupon-page', '_wpnonce' );
$post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : 0;
$post = get_post($post_id);
if (!$post) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Coupon is not defined.', 'formipay' ),
'icon' => 'error'
] );
}
if (!in_array($post->post_type, ['formipay-coupon'])) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Wrong Post Type.', 'formipay' ),
'icon' => 'error'
] );
}
$duplicate_post = [
'post_title' => $post->post_title . ' ' . __('Copy', 'formipay'),
'post_content' => $post->post_content,
'post_status' => 'draft', // Set sebagai draft
'post_type' => $post->post_type,
'post_author' => $post->post_author,
];
// Masukkan duplikat ke database
$new_post_id = wp_insert_post($duplicate_post);
if (is_wp_error($new_post_id)) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Something happened. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
// Salin semua meta data
$meta_data = get_post_meta($post_id);
foreach ($meta_data as $key => $values) {
if($key !== 'forms'){
foreach ($values as $value) {
add_post_meta($new_post_id, $key, maybe_unserialize($value));
}
}
}
wp_send_json_success( [
'title' => esc_html__('Duplicated!', 'formipay'),
'message' => esc_html__( 'Coupon is successfully duplicated.', 'formipay' ),
'icon' => 'success'
] );
}
}

451
includes/Customer.php Normal file
View File

@@ -0,0 +1,451 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Customer {
use SingletonTrait;
private $order_data;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'init', [$this, 'create_db'] );
add_action( 'admin_menu', [$this, 'add_menu'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue'] );
add_action( 'wp_ajax_formipay-tabledata-customers', [$this, 'formipay_tabledata_customers'] );
add_action( 'wp_ajax_formipay_check_for_data_mapping_saved_value', [$this, 'check_data_mapping'] );
// Add Global & Form Settings
// add_filter( 'formipay/global-settings', [$this, 'global_settings'] );
add_filter( 'formipay/global-settings/tab:general', [$this, 'global_settings'], 20 );
add_filter( 'formipay/form-config', [$this, 'add_menu_on_product_setting'], 100 );
// Register new customer by order submitted
add_action( 'formipay/order/new', [$this, 'add_customer'] );
}
public function create_db() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$create[] = "CREATE TABLE `{$wpdb->base_prefix}formipay_customers` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) DEFAULT 0,
`registered_date` datetime DEFAULT '00-00-00 00:00:00',
`name` text,
`phone` text,
`email` text,
`purchase_value` float(10, 4) DEFAULT 0,
`purchase_count` int,
`meta_data` text,
PRIMARY KEY (`id`)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($create);
}
public function submit($args) {
$args = wp_parse_args( $args, [
'user_id' => 0,
'registered_date' => '',
'name' => NULL,
'phone' => NULL,
'email' => NULL,
'purchase_value' => 0,
'purchase_count' => 0,
'meta_data' => []
] );
global $wpdb;
$table = $wpdb->prefix . 'formipay_customers';
$insert_data = [
'user_id' => intval($args['user_id']),
'registered_date' => formipay_date('Y-m-d H:i:s'),
'name' => sanitize_text_field( $args['name'] ),
'phone' => sanitize_text_field( $args['phone'] ),
'email' => sanitize_email( $args['email'] ),
'purchase_value' => floatval($args['purchase_value']),
'purchase_count' => intval($args['purchase_count']),
'meta_data' => maybe_serialize($args['meta_data'])
];
if($args['name'] !== ''){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert($table, $insert_data);
return $wpdb->insert_id;
}
return false;
}
public function get($customer_id = 0) {
global $wpdb;
$table = $wpdb->prefix .'formipay_customers';
if($customer_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 `id` = %d", $table, $customer_id
)
);
}
return $get;
}
public function get_by_email($email) {
global $wpdb;
$table = $wpdb->prefix .'formipay_customers';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM %i WHERE `email` = %s", $table, $email
), ARRAY_A
);
return $get;
}
public function update($customer_id, $args) {
$recent_data = $this->get($customer_id);
$args = wp_parse_args( $args, [
'user_id' => $recent_data->user_id,
'name' => $recent_data->name,
'phone' => $recent_data->phone,
'email' => $recent_data->email,
'purchase_value' => $recent_data->purchase_value,
'purchase_count' => $recent_data->purchase_count,
'meta_data' => maybe_unserialize( $recent_data->meta_data )
] );
global $wpdb;
$table = $wpdb->prefix . 'formipay_customers';
$insert_data = [
'user_id' => intval($args['user_id']),
'name' => sanitize_text_field( $args['name'] ),
'phone' => sanitize_text_field( $args['phone'] ),
'email' => sanitize_email( $args['email'] ),
'purchase_value' => floatval($args['purchase_value']),
'purchase_count' => intval($args['purchase_count']),
'meta_data' => maybe_serialize($args['meta_data'])
];
$where = [ 'id' => $customer_id ];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$update = $wpdb->update( $table_name, $new_args, $where );
return $update;
}
public function add_menu() {
add_submenu_page(
'formipay',
__( 'Customers', 'formipay' ),
__( 'Customers', 'formipay' ),
'manage_options',
'formipay-customers',
[$this, 'customers_page']
);
}
public function enqueue() {
global $current_screen;
if($current_screen->id == 'formipay_page_formipay-customers') {
$customer_id = intval(filter_input(INPUT_GET, 'customer_id', FILTER_SANITIZE_STRING));
if(empty($customer_id)){
wp_enqueue_style( 'page-customers', FORMIPAY_URL . 'admin/assets/css/admin-customers.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'page-customers', FORMIPAY_URL . 'admin/assets/js/admin-customers.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
}else{
wp_enqueue_style( 'bootstrap-icon', FORMIPAY_URL . 'vendor/Bootstrap/bootstrap-icons.css', [], '1.11.1', 'all');
wp_enqueue_style( 'bootstrap', FORMIPAY_URL . 'vendor/Bootstrap/bootstrap.min.css', [], '5.3.2' );
wp_enqueue_style( 'page-customers', FORMIPAY_URL . 'admin/assets/css/admin-customer-details.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'handlebars', FORMIPAY_URL . 'vendor/HandleBars/handlebars.min.js', [], '4.7.7', true);
wp_enqueue_script( 'bootstrap', FORMIPAY_URL . 'vendor/Bootstrap/bootstrap.bundle.min.js', ['jquery'], '5.3.2', true );
wp_enqueue_script( 'page-customers', FORMIPAY_URL . 'admin/assets/js/admin-customer-details.js', ['jquery'], FORMIPAY_VERSION, true );
}
wp_localize_script( 'page-customers', 'formipay_customers_page', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
'customer_id' => $customer_id,
'columns' => [
'id' => esc_html__( 'ID', 'formipay' ),
'name' => esc_html__( 'Name', 'formipay' ),
'email' => esc_html__( 'Email', 'formipay' ),
'phone' => esc_html__( 'Phone', 'formipay' ),
'total_order' => esc_html__( 'Total Order', 'formipay' ),
]
] );
}
}
public function customers_page() {
$customer_id = intval(filter_input(INPUT_GET, 'customer_id', FILTER_SANITIZE_STRING));
if(empty($customer_id)){
include FORMIPAY_PATH . 'admin/page-customers.php';
}else{
include FORMIPAY_PATH . 'admin/page-customer-details.php';
}
}
public function formipay_tabledata_customers() {
$get_all_customers = $this->get();
$customers = [];
if(!empty($get_all_customers)){
foreach($get_all_customers as $customer){
$customers[] = [
'ID' => $customer->id,
'name' => $customer->name,
'email' => $customer->email,
'phone' => $customer->phone,
'total_order' => $customer->purchase_count
];
}
}
// Prepare response data
$response = [
'results' => $customers,
'total' => count($get_all_customers),
'posts_report' => $customers
];
wp_send_json($response);
}
public function check_data_mapping() {
check_ajax_referer( 'formipay-form-editor', '_wpnonce' );
$form_id = isset($_REQUEST['post']) ? intval($_REQUEST['post']) : 0;
$saved = [
'buyer_name' => formipay_get_post_meta($form_id, 'buyer_name'),
'buyer_country' => formipay_get_post_meta($form_id, 'buyer_country')
];
$medias = apply_filters('formipay/notification/media', ['email', 'phone'] );
if(!empty($medias)){
foreach($medias as $media) {
$saved['buyer_'.$media] = formipay_get_post_meta($form_id, 'buyer_'.$media);
}
}
wp_send_json($saved);
}
public function global_settings($fields) {
$config = array(
'customer_field_group' => array(
'type' => 'group_title',
'label' => __( 'Customer Data', 'formipay' ),
'group' => 'started'
),
'customer_mandatory_data' => array(
'type' => 'sorter',
'label' => __( 'Identities', 'formipay' ),
'options' => array(
array(
'id' => 'optional',
'name' => esc_html__( 'Optional', 'formipay' ),
'options' => array(
array(
'id' => 'phone',
'label' => esc_html__( 'Phone', 'formipay' ),
),
)
),
array(
'id' => 'mandatory',
'name' => esc_html__( 'Mandatory', 'formipay' ),
'options' => array(
array(
'id' => 'name',
'label' => esc_html__( 'Name', 'formipay' ),
'icon' => 'fa fa-exclamation',
'class' => 'disable'
),
array(
'id' => 'email',
'label' => esc_html__( 'Email', 'formipay' ),
),
)
)
),
'group' => 'ended'
)
);
$fields = array_merge($fields, $config);
// $customer_settings = apply_filters( 'formipay/global-settings/tab:customer', $config );
// if(!empty($customer_settings)){
// $fields['customer'] = array(
// 'name' => __('Customer', 'formipay'),
// 'fields' => $customer_settings
// );
// }
return $fields;
}
public function add_menu_on_product_setting($fields) {
$medias = apply_filters('formipay/notification/media', ['email', 'phone'] );
$customer_mandatory_data = formipay_customer_mandatory_data();
$customer_fields = [
'notification_buyer_identify' => [
'type' => 'group_title',
'label' => __( 'Data Mapping', 'formipay' ),
'group' => 'started',
'description' => __( 'Match the required data for store the customer data, render shortcode and sending notification (if any)', 'formipay' ),
],
'buyer_name' => [
'type' => 'select',
'label' => __( 'Buyer\'s Name Field ID', 'formipay' ),
'description' => __( 'Fill in the field ID that represents the buyer\'s name.', 'formipay' ),
'required' => true
]
];
foreach($medias as $media){
$customer_fields['buyer_'.$media] = [
'type' => 'select',
// translators: %s is the media type name.
'label' => sprintf( __('Buyer\'s %s Field ID', 'formipay' ), ucfirst($media) ),
// translators: %s is the media type name.
'description' => sprintf( __( 'Fill in the field ID that represents the buyer\'s %s.', 'formipay' ), ucfirst($media) ),
'required' => in_array($media, $customer_mandatory_data) ? true : false
];
}
if(in_array('phone', $medias)){
$customer_fields['buyer_allow_choose_country_code'] = [
'type' => 'checkbox',
'label' => __('Buyer\'s can choose their own country code', 'formipay' ),
'value' => true,
'required' => in_array('phone', $customer_mandatory_data) ? true : false
];
$customer_fields['buyer_country'] = [
'type' => 'select',
'label' => __('Buyer\'s Country Field', 'formipay' ),
'description' => __( 'Fill in the field ID that represents the buyer\'s country. This will define the country phone code.', 'formipay' ),
'required' => in_array('phone', $customer_mandatory_data) ? true : false,
'dependency' => array(
'key' => 'buyer_allow_choose_country_code',
'value' => 'not_empty'
)
];
$customer_fields['buyer_phone_country_code'] = [
'type' => 'select',
'label' => __('Buyer\'s Default Country Code', 'formipay' ),
'options' => formipay_phone_country_code_options(),
'required' => true,
'searchable' => true,
'required' => in_array('phone', $customer_mandatory_data) ? true : false,
'dependency' => array(
'key' => 'buyer_allow_choose_country_code',
'value' => 'empty'
)
];
}
$last_data_mapping_fields = array_key_last($customer_fields);
$customer_fields[$last_data_mapping_fields]['group'] = 'ended';
$fields['formipay_form_settings']['customer_data'] = [
'name' => __('Customer Data', 'formipay'),
'fields' => $customer_fields
];
return $fields;
}
public function add_customer($order_data) {
$this->order_data = $order_data;
$buyer_name_field = formipay_get_post_meta($this->order_data['form_id'], 'buyer_name');
$buyer_email_field = formipay_get_post_meta($this->order_data['form_id'], 'buyer_email');
$buyer_phone_field = formipay_get_post_meta($this->order_data['form_id'], 'buyer_phone');
// Fallback Array
$customer_data = [
'name' => '',
'email' => '',
'phone' => ''
];
if(!empty($buyer_name_field) && !empty($this->order_data['form_data'][$buyer_name_field])) {
$customer_data['name'] = $this->order_data['form_data'][$buyer_name_field]['value'];
}
if(!empty($buyer_email_field) && !empty($this->order_data['form_data'][$buyer_email_field])) {
$customer_data['email'] = $this->order_data['form_data'][$buyer_email_field]['value'];
}
if(!empty($buyer_phone_field) && !empty($this->order_data['form_data'][$buyer_phone_field])) {
$customer_data['phone'] = $this->order_data['form_data'][$buyer_phone_field]['value'];
}
$email_is_exist = false;
if(!empty($customer_data['email'])){
$check = $this->get_by_email($customer_data['email']);
if(!empty($check)){
$email_is_exist = true;
}
}
if(false == $email_is_exist && !empty($customer_data['name'])){
$add_customer = $this->submit($customer_data);
}
}
}

317
includes/Field.php Normal file
View File

@@ -0,0 +1,317 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Field {
use SingletonTrait;
// Render a text input field
public function render_text_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="text" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render an email input field
public function render_email_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="email" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a number input field
public function render_number_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="number" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a url field
public function render_url_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="url" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a telephone field
public function render_tel_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="tel" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a date field
public function render_date_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="date" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a datetime field
public function render_datetime_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="datetime-local" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a color field
public function render_color_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<input type="color" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" data-label="'.esc_attr($label).'"'.$required.' '.$calc.'>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a select dropdown field
public function render_select_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$placeholder = $args['placeholder'] ?? '';
$options = $args['field_options'] ?? [];
$default_value = $args['default_value'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
$options_html = '';
foreach ($options as $option) {
$value = $option['value'] ?? $option['label'];
$selected = ($default_value == $value) ? ' selected' : '';
$options_html .= '<option value="'.esc_attr($value).'"'.$selected.'>'.esc_html($option['label']).'</option>';
}
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<select id="'.esc_attr($field_id).'" name="'.esc_attr($field_id).'" class="formipay-input formipay-select'.$calculable.'" data-label="'.esc_attr($label).'" data-placeholder="'.esc_attr($placeholder).'"'.$required.' '.$calc.'>
'.$options_html.'
</select>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a checkbox group field
public function render_checkbox_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$options = $args['field_options'] ?? [];
$default_value = $args['default_value'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
$options_html = '';
foreach ($options as $index => $option) {
$value = $option['value'] ?? $option['label'];
$checked = ($default_value == $value) ? ' checked' : '';
$options_html .= '
<label for="'.esc_attr($field_id.'-'.$index).'" class="formipay-label">
<input type="checkbox" name="'.esc_attr($field_id).'[]" id="'.esc_attr($field_id.'-'.$index).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'"'.$checked.$required.' '.$calc.'>
<span>'.esc_html($option['label']).'</span>
</label>';
}
return '
<div class="formipay-field-group">
<label class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<div class="formipay-checkbox-field-group">'.$options_html.'</div>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a radio group field
public function render_radio_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$options = $args['field_options'] ?? [];
$default_value = $args['default_value'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
$options_html = '';
foreach ($options as $index => $option) {
$value = $option['value'] ?? $option['label'];
$checked = ($default_value == $value) ? ' checked' : '';
$options_html .= '
<label for="'.esc_attr($field_id.'-'.$index).'" class="formipay-label">
<input type="radio" name="'.esc_attr($field_id).'" id="'.esc_attr($field_id.'-'.$index).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'"'.$checked.$required.' '.$calc.'>
<span>'.esc_html($option['label']).'</span>
</label>';
}
return '
<div class="formipay-field-group">
<label class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<div class="formipay-radio-field-group">'.$options_html.'</div>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a textarea field
public function render_textarea_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$placeholder = $args['placeholder'] ?? '';
$value = $args['default_value'] ?? '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group">
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<textarea class="formipay-input'.$calculable.'" name="'.esc_attr($field_id).'" placeholder="'.esc_attr($placeholder).'" data-label="'.esc_attr($label).'"'.$required.' rows="4" '.$calc.'>'.esc_html($value).'</textarea>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a hidden input field
public function render_hidden_field($args) {
$value = $args['default_value'] ?? '';
$field_id = $args['field_id'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '<input type="hidden" name="'.esc_attr($field_id).'" class="formipay-input'.$calculable.'" value="'.esc_attr($value).'" '.$calc.'>';
}
// Render a divider field
public function render_divider($args) {
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group'.$calculable.'" '.$calc.'>
<label for="'.esc_attr($field_id).'" class="formipay-label divider-label">'.$label.'</label>
<span class="divider-line"></span>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>';
}
// Render a page break field
public function render_page_break($args) {
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$description = $args['description'] ?? '';
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
return '
<div class="formipay-field-group formipay-page-break'.$calculable.'" '.$calc.'>
<div class="formipay-page-break-information">
<label for="'.esc_attr($field_id).'" class="formipay-label divider-label">'.$label.'</label>
<span class="divider-line"></span>
'.(!empty($description) ? '<p class="formipay-input-desc">'.esc_html($description).'</p>' : '').'
</div>
</div>';
}
// Render a country list select field
public function render_country_list_field($args) {
$required = !empty($args['is_required']) && $args['is_required'] === 'yes' ? ' required' : '';
$label = $args['label'] ?? '';
$field_id = $args['field_id'] ?? '';
$placeholder = $args['placeholder'] ?? '';
$countries = function_exists('formipay_country_array') ? formipay_country_array() : [];
$calculable = $args['calculable'] ?? '';
$calc = $args['calc'] ?? '';
$options_html = '';
foreach ($countries as $country) {
$options_html .= '<option value="'.esc_attr($country['name']).'" data-phone-code="'.esc_attr($country['phone']).'">'.esc_html($country['name']).'</option>';
}
return '
<div class="formipay-field-group preset select-searchable'.$calculable.'" '.$calc.'>
<label for="'.esc_attr($field_id).'" class="formipay-label">'.$label.($required ? ' <span class="formipay-asterisk">*</span>' : '').'</label>
<select id="'.esc_attr($field_id).'" name="'.esc_attr($field_id).'" class="formipay-input formipay-select" data-label="'.esc_attr($label).'" data-placeholder="'.esc_attr($placeholder).'"'.$required.'>
'.$options_html.'
</select>
</div>';
}
}

1761
includes/Form.php Normal file

File diff suppressed because it is too large Load Diff

206
includes/Init.php Normal file
View File

@@ -0,0 +1,206 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Init {
use SingletonTrait;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'admin_menu', [$this, 'menu'] );
add_action( 'admin_footer', [$this, 'admin_global_custom_js'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue'] );
add_action( 'plugin_loaded', [$this, 'load_nuxy'] );
add_action( 'plugin_loaded', [$this, 'init'] );
add_action( 'plugin_loaded', [$this, 'default_config'] );
}
public function init() {
// Integration Class
// \Formipay\Integration\Paypal::get_instance();
// Payment classes
\Formipay\Payment\BankTransfer::get_instance();
\Formipay\Payment\CashOnDelivery::get_instance();
// Shipping classes
\Formipay\Shipping\FlatRate::get_instance();
// Notification classes
\Formipay\Notification\Notification::get_instance();
\Formipay\Notification\Email::get_instance();
// Core and other classes
\Formipay\Field::get_instance();
\Formipay\Form::get_instance();
\Formipay\Product::get_instance();
\Formipay\Access::get_instance();
\Formipay\Coupon::get_instance();
\Formipay\Settings::get_instance();
\Formipay\Order::get_instance();
\Formipay\Customer::get_instance();
\Formipay\Render::get_instance();
\Formipay\Thankyou::get_instance();
new \Formipay\Token;
do_action('formipay_init');
}
public function menu() {
add_menu_page(
'Formipay',
'Formipay',
'manage_options',
'formipay',
'',
FORMIPAY_URL . 'admin/assets/img/White.png',
5
);
}
public function load_nuxy() {
require_once( FORMIPAY_PATH . 'vendor/wpcfto/WPCFTO.php');
}
public function enqueue() {
global $current_screen;
$formipayCPTs = [
'formipay-form' => __( 'Edit Form', 'formipay' ),
'formipay-product' => __( 'Edit Product', 'formipay' ),
'formipay-coupon' => __( 'Edit Coupon', 'formipay' ),
'formipay-access' => __( 'Edit Access', 'formipay' )
];
wp_enqueue_style( 'formipay-admin-global', FORMIPAY_URL . 'admin/assets/css/admin-global.css', [], FORMIPAY_VERSION, 'all');
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
if(strpos($current_screen->id, 'formipay') !== false){
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.mermaid.min.css', [], '6.2.0', 'all' );
wp_enqueue_script( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.js', [], FORMIPAY_VERSION, true );
wp_enqueue_script( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/js/admin-pages.js', ['jquery'], FORMIPAY_VERSION, true );
wp_enqueue_script( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.production.min.js', ['jquery'], '6.2.0', true );
}
if(in_array($current_screen->post_type, array_keys($formipayCPTs)) && $current_screen->base == 'post'){
wp_enqueue_style( 'formipay-classic-editor', FORMIPAY_URL . 'admin/assets/css/classic-editor.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'formipay-classic-editor', FORMIPAY_URL . 'admin/assets/js/classic-editor.js', ['jquery'], FORMIPAY_VERSION, true);
wp_enqueue_script( 'formipay-screen-menu', FORMIPAY_URL . 'admin/assets/js/admin-all-post-type.js', ['jquery'], FORMIPAY_VERSION, true);
wp_localize_script( 'formipay-screen-menu', 'formipay_admin', [
'site_url' => site_url(),
'page_title' => $formipayCPTs[$current_screen->post_type]
] );
}
}
public function admin_global_custom_js() {
global $pagenow, $current_screen;
?>
<script class="formipay-adminjs-global">
jQuery(function($){
<?php
// Formipay Form CPT
if( $current_screen->post_type == 'formipay-form' ) {
?>
$('#toplevel_page_formipay').removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open');
$('a[href="admin.php?page=formipay"]').closest('li').addClass('current');
<?php
}
// Formipay Access CPT
if( $current_screen->post_type == 'formipay-product' ) {
?>
$('#toplevel_page_formipay').removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open');
$('a[href="admin.php?page=formipay-product"]').closest('li').addClass('current');
<?php
}
// Formipay Access CPT
if( $current_screen->post_type == 'formipay-access' ) {
?>
$('#toplevel_page_formipay').removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open');
$('a[href="admin.php?page=formipay-access-items"]').closest('li').addClass('current');
<?php
}
// Formipay Coupon CPT
if( $current_screen->post_type == 'formipay-coupon' ) {
?>
$('#toplevel_page_formipay').removeClass('wp-not-current-submenu').addClass('wp-has-current-submenu wp-menu-open');
$('a[href="admin.php?page=formipay-coupon"]').closest('li').addClass('current');
<?php
}
?>
});
</script>
<?php
}
public function default_config() {
$array = array (
'thankyou_link' => 'thanks',
'thankyou_style' => 'receipt',
'thankyou_page_container_bg_color' => '#808080',
'thankyou_page_wrapper_bg_color' => '#ffffff',
'thankyou_page_wrapper_max_width' => '600',
'thankyou_confirmation_dropzone_color' => '#cccccc',
'thankyou_confirmation_dropzone_text_color' => '#808080',
'thankyou_confirmation_dropzone_instruction' => 'Drag & drop a file here or click to select one',
'thankyou_confirmation_submit_button' => 'Upload',
'cod_instruction_thankyou' => '<p>Your package will be shipped as soon as possible. Please make sure your&nbsp;<span style="background-color: transparent; color: var(--bs-body-color); text-align: var(--bs-body-text-align);">cash of </span><span style="background-color: transparent; color: var(--bs-body-color); text-align: var(--bs-body-text-align);">{{grand_total}} will be prepared&nbsp;</span><span style="background-color: transparent; color: var(--bs-body-color); text-align: var(--bs-body-text-align);">and you can pay it to the courier who delivers your&nbsp;</span><span style="background-color: transparent; color: var(--bs-body-color); text-align: var(--bs-body-text-align);">{{product_name}}.</span></p>',
'cod_instruction_whatsapp' => 'I will prepare the cash of {{grand_total}} for paying the order to the courier who delivers my {{product_name}}.',
'cod_confirmation_page_group' => '',
'cod_confirmation_page_content' => '<h3 style="text-align: left; margin-bottom: unset; padding: 0px; border: none; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: var(--theme-line-height); font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; --theme-font-weight: 700; --theme-font-size: 30px; --theme-line-height: 1.5;">Confirm Your Order</h3><h6 style="text-align: left; margin-bottom: calc(var(--has-theme-content-spacing, 1)*(.3em + 10px)); padding: 0px; border: none; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: var(--theme-line-height); font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; --theme-font-weight: 700; --theme-font-size: 16px; --theme-line-height: 1.5;">Cash on Delivery</h6><p style="text-align: center; margin-bottom: calc(var(--has-theme-content-spacing, 1)*(.3em + 10px)); padding: 0px; border: none; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: var(--theme-line-height); font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; --theme-font-weight: 700; --theme-font-size: 16px; --theme-line-height: 1.5;"><p style="text-align: left;"><span style="background-color: transparent; color: var(--bs-body-color);">Here is your order details:</span></p><p style="text-align: left;"><span style="background-color: transparent; color: var(--bs-body-color);">{{order_details}}</span></p></p><p style="text-align: center; margin-bottom: calc(var(--has-theme-content-spacing, 1)*(.3em + 10px)); padding: 0px; border: none; font-variant-numeric: inherit; font-variant-east-asian: inherit; font-variant-alternates: inherit; font-variant-position: inherit; font-stretch: inherit; line-height: var(--theme-line-height); font-optical-sizing: inherit; font-kerning: inherit; font-feature-settings: inherit; font-variation-settings: inherit; --theme-font-weight: 700; --theme-font-size: 16px; --theme-line-height: 1.5;"><p style="text-align: left;"><span style="background-color: transparent; color: var(--bs-body-color);">Please confirm if the package has been arrived</span></p><p style="text-align: left;"><span style="background-color: transparent; color: var(--bs-body-color);">{{confirmation_form}}</span></p></p>',
'cod_confirmation_page_width' => '600',
'cod_confirmation_table_alignment' => 'left',
'cod_confirmation_form_agreement' => 'I have receipt the package and pay as billed to the courier.',
'cod_confirmation_form_button' => 'Confirm',
'notification_email_footer' => '<p style="text-align: center;">You got this email because of ordering our products in Formipay.<br>Ignore this email if it was not you.</p>',
'notification_email_admin_on_hold_group_header' => '',
'notification_email_admin_on_hold_toggle' => true,
'notification_email_admin_on_hold_title' => 'New Order On-Hold: Action Required',
'notification_email_admin_on_hold_content' => '<p>Hello Admin,</p><p><br></p><p>A new order has been placed and is currently on hold, awaiting payment from the buyer.&nbsp;</p><p><br></p><p><strong>Buyer Name:</strong>&nbsp;{{buyer_name}}&nbsp;&nbsp;</p><p><strong>Order ID:</strong> {{order_id}}</p><p><strong>Order Status:</strong> On Hold&nbsp;&nbsp;</p><p><br></p><p>{{order_details}}</p><p><br></p><p>Please review the order and follow up with the buyer to encourage them to complete their payment.</p><p><br></p><p>Best regards,</p><p>Your E-commerce Team</p>',
'notification_email_buyer_on_hold_group_header' => '',
'notification_email_buyer_on_hold_toggle' => true,
'notification_email_buyer_on_hold_title' => 'Your Order is On Hold: Action Needed',
'notification_email_buyer_on_hold_content' => '<p>Hello {{buyer_name}},</p><p>Thank you for your recent order! Your order is currently on hold because we have not yet received your payment.</p><p><strong>Order ID:</strong>{{order_id}}</p><p><strong>Order Status:</strong>On Hold</p><p><strong>Payment Timeout:</strong>{{payment_timeout}}</p><p><br></p><p>{{order_details}}</p><p><br></p><p>Please complete your payment within the next {{payment_timeout}} to avoid cancellation. If you have any questions, feel free to reach out.</p><p>Thank you for shopping with us!</p><p><br></p><p>Best,</p><p>Your E-commerce Team</p>',
'notification_email_admin_payment_confirm_group_header' => '',
'notification_email_admin_payment_confirm_toggle' => true,
'notification_email_admin_payment_confirm_title' => 'Payment Confirmed for Order: Immediate Attention Required',
'notification_email_admin_payment_confirm_content' => '<p>Hello Admin,</p><p><br></p><p>The buyer has confirmed payment for the following order:Buyer Name: {{buyer_name}}&nbsp;&nbsp;</p><p><br></p><p><strong>Buyer Name:</strong>**Buyer Name:* {{buyer_name}}&nbsp;&nbsp;</p><p><strong>Order ID:</strong>**Order ID: {{order_id}}&nbsp;&nbsp;</p><p><strong>Order Status:</strong>Order Status: Payment Confirmed&nbsp;&nbsp;</p><p><br></p><p>{{order_details}}&nbsp;&nbsp;</p><p><br></p><p>Please take the necessary steps to process this order and prepare for shipment.</p><p><br></p><p>Best regards,&nbsp;&nbsp;</p><p>Your E-commerce Team</p>',
'notification_email_buyer_payment_confirm_group_header' => '',
'notification_email_buyer_payment_confirm_toggle' => true,
'notification_email_buyer_payment_confirm_title' => 'Payment Confirmed for Your Order',
'notification_email_buyer_payment_confirm_content' => '<p>Hello {{buyer_name}},</p><p><br></p><p>We\'re excited to let you know that your payment has been confirmed!</p><p><strong><br></strong></p><p><strong>Order ID:</strong> {{order_id}}&nbsp;&nbsp;</p><p><strong>Order Status:</strong> Payment Confirmed&nbsp;&nbsp;</p><p><br></p><p>{{order_details}}<br></p><p><br></p><p>Our team is now preparing your order for shipment. You will receive another update once your items are on their way!</p><p><br></p><p>Thank you for choosing us!</p><p><br></p><p>Best,&nbsp;&nbsp;</p><p>Your E-commerce Team</p><p><br></p>',
'notification_email_admin_completed_toggle' => true,
'notification_email_admin_completed_title' => 'Order Completed: Final Update',
'notification_email_admin_completed_content' => '<p>Hello Admin,</p><p><br></p><p>The following order has been successfully completed:</p><p><strong><br></strong></p><p><strong>Buyer Name:</strong> {{buyer_name}}&nbsp;&nbsp;</p><p><strong>Order ID:</strong> {{order_id}}&nbsp;&nbsp;</p><p><strong>Order Status:</strong> Completed&nbsp;&nbsp;</p><p><br></p><p>{{order_details}}&nbsp;&nbsp;</p><p><br></p><p>No further action is required. Thank you for your continued support!</p><p><br></p><p>Best regards,&nbsp;&nbsp;</p><p>Your E-commerce Team</p><p><br></p>',
'notification_email_buyer_completed_toggle' => true,
'notification_email_buyer_completed_title' => 'Your Order is Complete!',
'notification_email_buyer_completed_content' => '<p>Hello {{buyer_name}},</p><p><br></p><p>Thank you for your order! We are pleased to inform you that your order has been completed.</p><p><br></p><p><strong>Order ID:</strong> {{order_id}}&nbsp;&nbsp;</p><p><strong>Order Status:</strong> Completed&nbsp;&nbsp;</p><p><br></p><p>{{order_details}}&nbsp;&nbsp;</p><p><br></p><p>We hope you enjoy your purchase! If you have any feedback or questions, please let us know.</p><p><br></p><p>Best,&nbsp;&nbsp;</p><p>Your E-commerce Team</p><p><br></p>',
);
if(empty(get_option('formipay_settings'))){
update_option('formipay_settings', $array);
}
}
}

View File

@@ -0,0 +1,898 @@
<?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;
}
}

View File

@@ -0,0 +1,821 @@
<?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( '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 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'];
// Prepare the payload
$payload = json_encode(array(
"intent" => "CAPTURE",
"purchase_units" => array(
array(
"amount" => array(
"currency_code" => $currency,
"value" => $grand_total, // Format amount
"breakdown" => $breakdown
),
"items" => $items
)
),
'payment_source' => array(
'paypal' => array(
'experience_context' => array(
'return_url' => site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($order_data['form_id'].':::'.$order_data['id'].':::'.$unique_id).'?paypal-status=success'),
'cancel_url' => site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($order_data['form_id'].':::'.$order_data['id'].':::'.$unique_id).'?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 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;
}
}

View File

@@ -0,0 +1,797 @@
<?php
namespace Formipay\Notification;
use Formipay\Traits\SingletonTrait;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class Email extends Notification {
use SingletonTrait;
private $formipay_settings;
private $order_data;
private $payment_timeout;
private $email_template = null;
protected function __construct() {
add_action('formipay/notification/order/email', [$this, 'notification'] );
add_action('formipay/notification/access/email', [$this, 'access_link'] );
add_action('formipay/notification/order/email/trigger-event', [$this, 'send_email'], 999 );
$this->formipay_settings = get_option('formipay_settings');
}
private function get_email_template() {
if ($this->email_template === null) {
$this->email_template = file_get_contents(FORMIPAY_PATH . 'notification/email_templates/email-page-template.html');
}
return $this->email_template;
}
public function notification($order_data) {
$formipay_settings = $this->formipay_settings;
$this->order_data = $order_data;
$recipient_type = apply_filters('formipay/notification/recipients', ['admin', 'buyer'] );
$order_status = str_replace('-', '_', $this->order_data['status']);
foreach($recipient_type as $recipient){
if(
false !== boolval($formipay_settings['notification_email_'.$recipient.'_'.$order_status.'_toggle']) &&
!empty($formipay_settings['notification_email_'.$recipient.'_'.$order_status.'_title']) &&
!empty($formipay_settings['notification_email_'.$recipient.'_'.$order_status.'_content'])
) {
$recipient_email = '';
$buyer_email_field = formipay_get_post_meta($this->order_data['form_id'], 'buyer_email');
if($recipient == 'admin' && false !== boolval($formipay_settings['notification_email_admin_recipient'])){
$recipient_email = $formipay_settings['notification_email_admin_recipient'];
}elseif( $recipient == 'buyer' && !empty($buyer_email_field)) {
$recipient_email = $this->order_data['form_data'][$buyer_email_field];
}
if( '' !== $recipient_email ){
$args = [
'order_id' => $this->order_data['id'],
'recipient_type' => $recipient,
'media' => 'email',
'notification_data' => [
'to' => $recipient_email,
'subject' => $formipay_settings['notification_email_'.$recipient.'_'.$order_status.'_title'],
'content' => $this->process_content($recipient)
],
'meta_data' => [
'request_date' => formipay_date('Y-m-d H:i:s')
]
];
$notification_id = parent::insert_notification_data($args);
if(false !== $notification_id){
wp_schedule_single_event( time() + 15, 'formipay/notification/order/email/trigger-event', [$notification_id] );
}
}
}
do_action( 'formipay/notification/order/email/trigger', $recipient, $order_data);
}
}
public function send_email($notification_id) {
$get = parent::get_notification_data_by_id($notification_id);
if(false !== $get){
$metadata = maybe_unserialize( $get->meta_data );
$get_data = maybe_unserialize( $get->notification_data );
$status = $get->status;
if(!empty($get_data)){
$send_mail = wp_mail(
sanitize_email( $get_data['to'] ),
$get_data['subject'],
$get_data['content'],
array('Content-Type: text/html; charset=UTF-8')
);
if(!is_wp_error($send_mail)){
$status = 'sent';
}else{
$status = 'error';
$metadata['error_message'] = $send_mail->get_error_messages();
}
$metadata['update_date'] = formipay_date('Y-m-d H:i:s');
$args = [
'status' => $status,
'meta_data' => $metadata
];
\Formipay_Notification::update_notification_data($notification_id, $args);
}
}
}
public function access_link($order_data) {
$formipay_settings = $this->formipay_settings;
$this->order_data = $order_data;
$buyer_email_field = formipay_get_post_meta($order_data['form_id'], 'notification_email_buyer_recipient');
if(
!empty($formipay_settings['notification_email_access_link_title']) &&
!empty($formipay_settings['notification_email_access_link_content']) &&
!empty($buyer_email_field)
) {
$recipient = 'buyer';
$recipient_email = '';
foreach($order_data['form_data'] as $form_data){
if($form_data['name'] == $buyer_email_field){
$recipient_email = $form_data['value'];
break;
}
}
if( '' !== $recipient_email ){
$send_mail = wp_mail(
sanitize_email( $recipient_email ),
$formipay_settings['notification_email_access_link_title'],
$this->process_content('buyer', 'access_link'),
array('Content-Type: text/html; charset=UTF-8')
);
}
}
do_action( 'formipay/notification/access/email/trigger', $recipient, $order_data);
}
public function process_content($recipient, $context='order') {
$formipay_settings = $this->formipay_settings;
$order_status = str_replace('-', '_', $this->order_data['status']);
$order_body_template = $formipay_settings['notification_email_' . $recipient . '_' . $order_status . '_content'];
$accesslink_body_template = $formipay_settings['notification_email_access_link_content'];
$body_template = $context == 'order' ? $order_body_template : $accesslink_body_template;
$buyer_name = formipay_get_post_meta($this->order_data['form_id'], 'buyer_name');
if(!empty($buyer_name)){
$buyer_name_field = $buyer_name;
$buyer_name = $this->order_data['form_data'][$buyer_name_field]['value'];
}
// Prepare the replacements for shortcodes
$replacements = [
'{{image_logo}}' => $this->get_image_logo(),
'{{email_content}}' => $this->get_content($body_template, $context),
'{{action_button}}' => $context == 'order' ? $this->get_action_button($recipient) : '',
'{{footer_message}}' => $this->get_footer_message(),
'{{social_links}}' => $this->get_social_links()
];
// Load the email template
$email_template = $this->get_email_template();
// Replace the shortcodes in the email template
$email_template = strtr($email_template, $replacements);
return $email_template;
}
private function get_content_depracated($body_template, $context) {
$form_id = $this->order_data['form_id'];
$order_data = $this->order_data;
$formipay_settings = $this->formipay_settings;
// Cache frequently used settings and meta values
$buyer_name_field = formipay_get_post_meta($form_id, 'buyer_name');
$payment_gateway = $order_data['form_data']['payment_gateway'];
$payment_timeout = !empty($formipay_settings[$payment_gateway . '_timeout'])
? formipay_date($order_data['created_date'] . ' + ' . $formipay_settings[$payment_gateway . '_timeout'] . ' minutes')
: '';
$lines = explode('<p><br></p>', $body_template);
$content = '';
foreach ($lines as $line) {
$line = trim($line);
if (strpos($line, '{{order_details}}') !== false) {
$content .= '<tr class="wp-block-editor-paragraphblock-v1">
<td valign="top" style="color:#5f5f5f;letter-spacing:0;word-break:normal;font-size: 1rem;padding:1rem 0;background-color:#ffffff;">';
$content .= $this->convert_order_details();
$content .= '</td></tr>';
} else {
$content .= '<tr class="wp-block-editor-paragraphblock-v1">
<td valign="top" style="color:#5f5f5f;letter-spacing:0;word-break:normal;font-size: 1rem;padding:1rem 2rem;background-color:#ffffff;">';
// Replace placeholders with actual data
$replacements = [
'{{buyer_name}}' => !empty($buyer_name_field) ? $order_data['form_data'][$buyer_name_field] : '',
'{{order_id}}' => $order_data['id'],
'{{order_status}}' => $order_data['status'],
'{{payment_timeout}}' => $payment_timeout
];
$line = strtr( $line, $replacements );
if ($context === 'access_link') {
$meta_data = $order_data['meta_data'];
$meta_session_id = '';
foreach($meta_data as $meta){
if($meta['name'] == 'session_id'){
$meta_session_id = $meta['value'];
break;
}
}
$thankyou_link = 'thankyou';
if(isset($formipay_settings['thankyou_link']) && !empty($formipay_settings['thankyou_link'])){
$thankyou_link = $formipay_settings['thankyou_link'];
}
$access_link = site_url('/'.$thankyou_link.'/' . base64_encode("$form_id:::{$order_data['id']}:::{$meta_session_id}"));
$line = str_replace(
[
'{{access_link}}',
'{{access_button}}'
],
[
$access_link,
$this->get_action_button('buyer', 'access_link', ['url' => $access_link, 'label' => 'Access Link'])
],
$line
);
}
$content .= $line;
$content .= '</td></tr>';
}
}
return $content;
}
private function get_content($body_template, $context) {
$form_id = $this->order_data['form_id'];
$order_data = $this->order_data;
$formipay_settings = $this->formipay_settings;
// Cache frequently used settings and meta values
$buyer_name_field = formipay_get_post_meta($form_id, 'buyer_name');
$payment_gateway = $order_data['form_data']['payment_gateway']['label'];
$this->payment_timeout = $payment_timeout = !empty($formipay_settings[$payment_gateway . '_timeout'])
? formipay_date($order_data['created_date'] . ' + ' . $formipay_settings[$payment_gateway . '_timeout'] . ' minutes')
: '';
$lines = explode('</p>', $body_template);
$content = '';
foreach ($lines as $line) {
$line = trim(str_replace('<p>', '', $line));
if(in_array($line, ['', '<br>'])) {
continue;
}
if (strpos($line, '{{order_details}}') !== false) {
$content .= '<table border="0" cellpadding="10" cellspacing="0" class="heading_block block-3" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad">
<h3 style="margin: 0; color: #101112; direction: ltr; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700; letter-spacing: normal; line-height: 120%; text-align: left; margin-top: 1rem; margin-bottom: 0; mso-line-height-alt: 28.799999999999997px;">
<span class="tinyMce-placeholder" style="word-break: break-word;">' . __( 'Order Details', 'formipay' ) . '</span>
</h3>
</td>
</tr>
</table>';
$content .= $this->convert_order_details();
} elseif (strpos($line, '{{order_items}}') !== false) {
$content .= '<table border="0" cellpadding="10" cellspacing="0" class="heading_block block-3" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad">
<h3 style="margin: 0; color: #101112; direction: ltr; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700; letter-spacing: normal; line-height: 120%; text-align: left; margin-top: 1rem; margin-bottom: 0; mso-line-height-alt: 28.799999999999997px;">
<span class="tinyMce-placeholder" style="word-break: break-word;">' . __( 'Order Items', 'formipay' ) . '</span>
</h3>
</td>
</tr>
</table>';
$content .= $this->convert_order_items();
} elseif (strpos($line, '{{buyer_details}}') !== false) {
$content .= '<table border="0" cellpadding="10" cellspacing="0" class="heading_block block-3" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad">
<h3 style="margin: 0; color: #101112; direction: ltr; font-family: Arial, Helvetica, sans-serif; font-size: 14px; font-weight: 700; letter-spacing: normal; line-height: 120%; text-align: left; margin-top: 1rem; margin-bottom: 0; mso-line-height-alt: 28.799999999999997px;">
<span class="tinyMce-placeholder" style="word-break: break-word;">' . __( 'Buyer Details', 'formipay' ) . '</span>
</h3>
</td>
</tr>
</table>';
$content .= $this->convert_buyer_details();
} else {
$content .= '<table border="0" cellpadding="10" cellspacing="0" class="paragraph_block block-12" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word;" width="100%"><tr><td class="pad"><div style="color:#101112;direction:ltr;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:400;letter-spacing:0px;line-height:120%;text-align:left;mso-line-height-alt:19.2px;">';
// Replace placeholders with actual data
$replacements = [
'{{buyer_name}}' => !empty($buyer_name_field) ? $order_data['form_data'][$buyer_name_field]['value'] : '',
'{{order_id}}' => $order_data['id'],
'{{order_status}}' => $order_data['status'],
'{{payment_timeout}}' => $payment_timeout
];
$line = strtr( $line, $replacements );
if ($context === 'access_link') {
$meta_data = $order_data['meta_data'];
$meta_session_id = '';
foreach($meta_data as $meta){
if($meta['name'] == 'session_id'){
$meta_session_id = $meta['value'];
break;
}
}
$thankyou_link = 'thankyou';
if(isset($formipay_settings['thankyou_link']) && !empty($formipay_settings['thankyou_link'])){
$thankyou_link = $formipay_settings['thankyou_link'];
}
$token = Token::generate(
$order_data['id'],
$form_id,
900 // 15-minute expiration
);
$access_link = site_url('/'.$thankyou_link.'/' . $token);
$line = str_replace(
[
'{{access_link}}',
'{{access_button}}'
],
[
$access_link,
$this->get_action_button('buyer', 'access_link', ['url' => $access_link, 'label' => __('Access Link', 'formipay')])
],
$line
);
}
$content .= $line;
$content .= '</div></td></tr></table>';
}
}
return $content;
}
// Function to get the logo image
private function get_image_logo() {
$formipay_settings = $this->formipay_settings;
if(empty($formipay_settings['notification_email_logo'])){
return;
}
$logo_url = wp_get_attachment_image_url($formipay_settings['notification_email_logo'], 'full');
if(false == $logo_url){
return;
}
/**
* Template Baru
*/
return '<table border="0" cellpadding="0" cellspacing="0" class="image_block block-1" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; margin: 2rem 0;" width="100%">
<tr>
<td class="pad" style="width:100%;">
<div align="center" class="alignment" style="line-height:10px">
<div style="max-width: 480px;">
' . wp_get_attachment_image($formipay_settings['notification_email_logo'], 'full', false, array(
'style' => 'display: block; height: auto; border: 0;',
'width' => '360'
)) . '
</div>
</div>
</td>
</tr>
</table>';
}
// Function to convert order details
private function convert_order_details_depracated() {
if(empty($this->order_data['items'])){
return;
}
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
ob_start();
?>
<table role="presentation" align="left" border="0" class="single-column" width="320" style="width:100%;float:left;border-collapse:collapse !important" cellspacing="0" cellpadding="0">
<tbody>
<?php foreach($this->order_data['items'] as $item){ ?>
<tr class="wp-block-editor-paragraphblock-v1" style="border-bottom: 1px solid #CECECE">
<td valign="top" style="padding:20px 20px 20px 2rem;background-color:#ffffff">
<p class="paragraph" style="font-size:1rem;margin:0;color:#5f5f5f;letter-spacing:0;word-break:normal;">
<span style="font-weight: bold; line-height: 1.25rem;" class="bold"><?php echo esc_html($item['item']); ?></span>
</p>
</td>
<td valign="top" style="padding:20px 2rem 20px 20px;background-color:#ffffff">
<p class="paragraph" style="font-size:1rem;margin:0;color:#5f5f5f;letter-spacing:0;word-break:normal;text-align:right;min-width:175px;">
<?php echo esc_html(formipay_price_format($item['subtotal'], $this->order_data['form_id'])); ?>
</p>
</td>
</tr>
<?php } ?>
</tbody>
<tfoot>
<tr class="wp-block-editor-paragraphblock-v1">
<td valign="top" style="padding:20px 20px 20px 2rem;background-color:#ffffff">
<p class="paragraph" style="font-size:1rem;margin:0;color:#5f5f5f;letter-spacing:0;word-break:normal;">
<span style="font-weight: bold; line-height: 1.25rem;" class="bold"><?php echo esc_html__('Total', 'formipay'); ?></span>
</p>
</td>
<td valign="top" style="padding:20px 2rem 20px 20px;background-color:#ffffff;min-width:175px;">
<p class="paragraph" style="color:<?php echo esc_html($button_background_color['active']); ?>;text-align:right;font-size:1.5rem;margin:0;letter-spacing:0;word-break:normal;">
<span style="font-weight: bold" class="bold">
<?php echo esc_html(formipay_price_format($this->order_data['total'], $this->order_data['form_id'])); ?>
</span>
</p>
</td>
</tr>
</tfoot>
</table>
<?php
$order_details_html = ob_get_contents();
ob_end_clean();
return $order_details_html;
}
// Function to convert order details
private function convert_order_details() {
if(empty($this->order_data['items'])){
return;
}
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
ob_start();
?>
<table border="0" cellpadding="10" cellspacing="0" class="table_block block-4" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad" style="padding: 0;">
<div style="border: 1px solid #cccccc; border-radius: 15px; padding: 15px;">
<table style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; border-collapse: collapse; width: 100%; table-layout: fixed; direction: ltr; background-color: transparent; font-family: Arial, Helvetica, sans-serif; font-weight: 400; color: #101112; text-align: left; letter-spacing: 0px;" width="100%">
<tbody style="vertical-align: top; font-size: 16px; line-height: 120%;">
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html__( 'Order ID', 'formipay' ); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo intval($this->order_data['id']); ?></td>
</tr>
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html__( 'Order Status', 'formipay' ); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html($this->order_data['status']); ?></td>
</tr>
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html__( 'Payment Timeout', 'formipay' ); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html($this->payment_timeout); ?> </td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
<?php
$order_details_html = ob_get_contents();
ob_end_clean();
return $order_details_html;
}
// Function to convert order items
private function convert_order_items() {
if(empty($this->order_data['items'])){
return;
}
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
ob_start();
?>
<table border="0" cellpadding="10" cellspacing="0" class="table_block block-6" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad" style="padding: 0;">
<div style="border: 1px solid #cccccc; border-radius: 15px; padding: 15px;">
<table style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; border-collapse: collapse; width: 100%; table-layout: fixed; direction: ltr; background-color: transparent; font-family: Arial, Helvetica, sans-serif; font-weight: 400; color: #101112; text-align: left; letter-spacing: 0px;" width="100%">
<thead style="vertical-align: top; font-size: 16px; line-height: 120%;">
<tr>
<th style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html__( 'Item', 'formipay' ); ?></th>
<th style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html__( 'Amount', 'formipay' ); ?></th>
</tr>
</thead>
<tbody style="vertical-align: top; font-size: 16px; line-height: 120%;">
<?php foreach($this->order_data['items'] as $item){ ?>
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html($item['item']); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html(formipay_price_format($item['subtotal'], $this->order_data['form_id'])); ?></td>
</tr>
<?php } ?>
<tr style="border-top: 1px solid #ccc">
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%">
<strong style="color: <?php echo esc_html($button_background_color['regular']); ?>;"><?php echo esc_html__( 'Total', 'formipay' ); ?></strong>
</td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%">
<strong style="color: <?php echo esc_html($button_background_color['regular']); ?>;"><?php echo esc_html(formipay_price_format($this->order_data['total'], $this->order_data['form_id'])); ?></strong>
</td>
</tr>
</tbody>
</table>
</div>
</td>
</tr>
</table>
<?php
$order_items_html = ob_get_contents();
ob_end_clean();
return $order_items_html;
}
// Function to convert buyer details
private function convert_buyer_details() {
if(empty($this->order_data['items'])){
return;
}
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
// $buyer_email_field = formipay_get_post_meta($this->order_data['form_id'], 'notification_email_buyer_recipient');
$field_buyer_name = formipay_get_post_meta($this->order_data['form_id'], 'buyer_name');
$buyer_name = !empty($this->order_data['form_data'][$field_buyer_name]) ? $this->order_data['form_data'][$field_buyer_name] : '';
ob_start();
?>
<table border="0" cellpadding="10" cellspacing="0" class="table_block block-8" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad" style="padding: 0;">
<div style="border: 1px solid #cccccc; border-radius: 15px; padding: 15px;">
<table style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; border-collapse: collapse; width: 100%; table-layout: fixed; direction: ltr; background-color: transparent; font-family: Arial, Helvetica, sans-serif; font-weight: 400; color: #101112; text-align: left; letter-spacing: 0px;" width="100%">
<tbody style="vertical-align: top; font-size: 16px; line-height: 120%;">
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html__( 'Name', 'formipay' ); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html($buyer_name); ?></td>
</tr>
<?php
$customer_data = apply_filters('formipay/customer/data-mapping', ['email', 'phone'] );
foreach($customer_data as $data){
if(false !== formipay_get_post_meta($this->order_data['form_id'], 'notification_'.$data.'_buyer_recipient')) {
$field_name = formipay_get_post_meta($this->order_data['form_id'], 'notification_'.$data.'_buyer_recipient');
$field_value = !empty($order_data['form_data'][$field_name]) ? $order_data['form_data'][$field_name] : '';
if($field_value !== ''){
?>
<tr>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd;" width="50%"><?php echo esc_html(ucwords($data)); ?></td>
<td style="padding: 10px; word-break: break-word; border-top: 0px solid #dddddd; border-right: 0px solid #dddddd; border-bottom: 0px solid #dddddd; border-left: 0px solid #dddddd; text-align: right;" width="50%"><?php echo esc_html($field_value); ?></td>
</tr>
<?php
}
}
}
?>
</tbody>
</table>
</div>
</td>
</tr>
</table>
<?php
$buyer_details_html = ob_get_contents();
ob_end_clean();
return $buyer_details_html;
}
private function get_action_button_depracated($recipient, $context='order', $button=[]) {
if($context == 'order' && empty($button)) {
$button = apply_filters( 'formipay/notification/order/email/action-button', [], $recipient, $this->order_data );
}
$content = '';
if(!empty($button)){
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
$button_text_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_text_color'), true );
ob_start();
if($context == 'order'){
?>
<tr class="wp-block-editor-buttonblock-v1" align="center">
<td style="background-color:#ffffff;padding-top:40px;padding-right:20px;padding-bottom:60px;padding-left:20px;width:100%" valign="top">
<table role="presentation" cellspacing="0" cellpadding="0" class="button-table">
<tbody>
<tr>
<td valign="top" class="button-CzEb4_za43eNTBt_C1QR7 button-td button-td-primary" style="cursor:pointer;border:none;border-radius:4px;background-color:<?php echo esc_html($button_background_color['regular']); ?>;font-size:16px;font-family:Open Sans, sans-serif;width:fit-content;text-decoration:none;color:<?php echo esc_html($button_text_color['regular']); ?>;overflow:hidden">
<a href="<?php echo esc_url($button['url']); ?>" style="color:<?php echo esc_html($button_text_color['regular']); ?>;display:block;padding:16px 16px 16px 16px"><?php echo esc_html($button['label']); ?></a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
<?php
}elseif($context == 'access_link'){
?>
<a href="<?php echo esc_url($button['url']); ?>" style="cursor:pointer;border:none;border-radius:4px;background-color:<?php echo esc_html($button_background_color['regular']); ?>;font-size:16px;font-family:Open Sans, sans-serif;width:fit-content;text-decoration:none;color:<?php echo esc_html($button_text_color['regular']); ?>;display:block;padding:16px 16px 16px 16px"><?php echo esc_html($button['label']); ?></a>
<?php
}
$content = ob_get_contents();
ob_end_clean();
}
return $content;
}
private function get_action_button($recipient, $context='order', $button=[]) {
if($context == 'order' && empty($button)) {
$button = apply_filters( 'formipay/notification/order/email/action-button', [], $recipient, $this->order_data );
}
$content = '';
if(!empty($button)){
$button_background_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_bg_color'), true );
$button_text_color = json_decode(formipay_get_post_meta($this->order_data['form_id'], 'button_text_color'), true );
ob_start();
if($context == 'order'){
?>
<div class="spacer_block block-9" style="height:2rem;line-height:15px;font-size:1px;"></div>
<table border="0" cellpadding="10" cellspacing="0" class="button_block block-10" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad" style="padding: 0;">
<div align="center" class="alignment">
<a href="<?php echo esc_url($button['url']); ?>" style="color:#ffffff;text-decoration:none;" target="_blank">
<!--[if mso]>
<v:roundrect
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word" href="<?php echo esc_url($button['url']); ?>" style="height:52px;width:460px;v-text-anchor:middle;" arcsize="58%" fillcolor="#7747FF">
<v:stroke dashstyle="Solid" weight="0px" color="#7747FF"/>
<w:anchorlock/>
<v:textbox inset="0px,0px,0px,0px">
<center dir="false" style="color:#ffffff;font-family:sans-serif;font-size:16px">
<![endif]-->
<span class="button" style="background-color: <?php echo esc_html($button_background_color['regular']); ?>; border-bottom: 0px solid transparent; border-left: 0px solid transparent; border-radius: 30px; border-right: 0px solid transparent; border-top: 0px solid transparent; color:<?php echo esc_html($button_text_color['regular']); ?>; display: inline-block; font-family: Arial, Helvetica, sans-serif; font-size: 16px; font-weight: 400; mso-border-alt: none; padding: 10px 0; text-align: center; width: 100%; word-break: keep-all; letter-spacing: normal;">
<span style="color:<?php echo esc_html($button_text_color['regular']); ?>;word-break: break-word; line-height: 32px;"><?php echo esc_html($button['label']); ?></span>
</span>
<!--[if mso]>
</center>
</v:textbox>
</v:roundrect>
<![endif]-->
</a>
</div>
</td>
</tr>
</table>
<div class="spacer_block block-11" style="height:2rem;line-height:15px;font-size:1px;"></div>
<?php
}elseif($context == 'access_link'){
?>
<table border="0" cellpadding="10" cellspacing="0" class="heading_block block-3" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt;" width="100%">
<tr>
<td class="pad">
<a href="<?php echo esc_url($button['url']); ?>" style="cursor:pointer;border:none;border-radius:4px;background-color:<?php echo esc_html($button_background_color['regular']); ?>;font-size:16px;font-family:Open Sans, sans-serif;width:fit-content;text-decoration:none;color:<?php echo esc_html($button_text_color['regular']); ?>;display:block;padding:16px 16px 16px 16px"><?php echo esc_html($button['label']); ?></a>
</td>
</tr>
</table>
<?php
}
$content = ob_get_contents();
ob_end_clean();
}
return $content;
}
private function get_footer_message_depracated() {
$formipay_settings = $this->formipay_settings;
if(empty($formipay_settings['notification_email_footer'])){
return;
}
$footer = sprintf('<tr>
<td align="center" style="padding: 20px;">
<p>%s</p>
</td>
</tr>', wp_kses_post($formipay_settings['notification_email_footer']) );
return $footer;
}
private function get_footer_message() {
$formipay_settings = $this->formipay_settings;
if(empty($formipay_settings['notification_email_footer'])){
return;
}
$footer = '<table border="0" cellpadding="10" cellspacing="0" class="paragraph_block block-12" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word; margin-bottom: 2rem;" width="100%">
<tr>
<td class="pad">
<div style="color:#101112;direction:ltr;font-family:Arial, Helvetica, sans-serif;font-size:16px;font-weight:400;letter-spacing:0px;line-height:120%;text-align:left;mso-line-height-alt:19.2px;">
'.wp_kses_post($formipay_settings['notification_email_footer']).'
</div>
</td>
</tr>
</table>';
return $footer;
}
private function get_social_links() {
$formipay_settings = $this->formipay_settings;
$content = '';
if(!empty($formipay_settings['notification_email_footer_links'])){
$links = [];
foreach($formipay_settings['notification_email_footer_links'] as $link){
// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- This image is a plugin asset and not a WordPress attachment.
$links[] = '<a href="'.esc_url($link['link']).'" style="margin:0 10px;"><img src="' . FORMIPAY_URL . 'admin/assets/img/icons8-' . esc_html($link['type']) . '-50.png" width="24"></a>';
}
$content .= '<table border="0" cellpadding="10" cellspacing="0" class="paragraph_block block-12" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; word-break: break-word; margin-bottom: 2rem;" width="100%"><tr><td class="pad" style="text-align: center;">';
$content .= implode('', $links);
$content .= '</td></tr></table>';
}
return $content;
}
}

View File

@@ -0,0 +1,512 @@
<?php
namespace Formipay\Notification;
use Formipay\Traits\SingletonTrait;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class Notification {
use SingletonTrait;
protected function __construct() {
add_action( 'init', [$this, 'create_db'] );
add_filter( 'formipay/global-settings/tab:general', [$this, 'global_settings_general'], 40 );
add_filter( 'formipay/global-settings', [$this, 'add_menu_on_global_setting'], 40 );
add_filter( 'formipay/form-config', [$this, 'add_menu_on_product_setting'], 100 );
add_action( 'formipay/notification/order', [$this, 'order_trigger'] );
add_action( 'formipay/notification/access', [$this, 'access_trigger'] );
}
public function create_db() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$create[] = "CREATE TABLE `{$wpdb->base_prefix}formipay_notification_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int,
`customer_id` int,
`order_id` int,
`recipient_type` text,
`media` text,
`notification_data` longtext,
`status` text,
`meta_data` text,
PRIMARY KEY (`id`)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($create);
}
public function insert_notification_data($args) {
$args = wp_parse_args( $args, [
'order_id' => 0,
'recipient_type' => 'admin',
'media' => 'email',
'notification_data' => [],
'status' => 'pending',
'meta_data' => []
] );
global $wpdb;
$table = $wpdb->prefix . 'formipay_notification_log';
$insert_data = [
'order_id' => intval($args['order_id']),
'recipient_type' => sanitize_text_field($args['recipient_type']),
'media' => sanitize_text_field($args['media']),
'notification_data' => maybe_serialize($args['notification_data']),
'status' => sanitize_text_field($args['status']),
'meta_data' => maybe_serialize($args['meta_data'])
];
if($args['order_id'] > 0){
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$wpdb->insert($table, $insert_data);
return $wpdb->insert_id;
}
return false;
}
public function get_notification_data_by_id($notification_id) {
global $wpdb;
$table = $wpdb->prefix . 'formipay_notification_log';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$get = $wpdb->get_row(
$wpdb->prepare(
"SELECT * FROM %i WHERE `id` = %d", $table, $notification_id
)
);
return $get;
}
public function update_notification_data($notification_id, $args) {
$recent_data = $this->get_notification_data_by_id($notification_id);
$args = wp_parse_args( $args, [
'order_id' => $recent_data->order_id,
'recipient_type' => $recent_data->recipient_type,
'media' => $recent_data->media,
'notification_data' => maybe_unserialize($recent_data->notification_data),
'status' => $recent_data->status,
'meta_data' => maybe_unserialize($recent_data->meta_data)
] );
global $wpdb;
$table_name = $wpdb->prefix . 'formipay_notification_log';
$new_args = [
'order_id' => intval($args['order_id']),
'recipient_type' => sanitize_text_field($args['recipient_type']),
'media' => sanitize_text_field($args['media']),
'notification_data' => maybe_serialize($args['notification_data']),
'status' => sanitize_text_field($args['status']),
'meta_data' => maybe_serialize($args['meta_data'])
];
$where = [ 'id' => $notification_id ];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$update = $wpdb->update( $table_name, $new_args, $where );
return $update;
}
public function global_settings_general($fields) {
$medias = apply_filters('formipay/notification/media', ['email'] );
$notification_fields = [
'notification_type_group' => [
'type' => 'group_title',
'label' => __( 'Notification Type', 'formipay'),
'group' => 'started',
],
];
foreach($medias as $key => $media){
$notification_fields['notification_'.$media.'_active'] = [
'type' => 'checkbox',
'label' => sprintf( __( '%s Notification', 'formipay'), ucfirst($media) ),
'value' => true
];
}
$last_notification_field = count($notification_fields) - 1;
$notification_fields[$last_notification_field]['group'] = 'ended';
$notification_fields['notification_email_sender_group_header'] = [
'type' => 'group_title',
'label' => __( 'Email Settings', 'formipay'),
'group' => 'started',
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
foreach($medias as $key => $media){
$notification_fields['notification_'.$media.'_admin_recipient'] = [
'type' => 'text',
// translators: %s is the media type name.
'label' => sprintf( __( 'Admin\'s %s', 'formipay'), ucfirst($media) ),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
}
$notification_fields['notification_name_admin_recipient'] = [
'type' => 'text',
'label' => __( 'Admin\'s Name', 'formipay'),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
$notification_fields['notification_email_logo'] = [
'type' => 'image',
'label' => __('Logo Image', 'formipay'),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
$notification_fields['notification_email_footer'] = [
'type' => 'tinymce',
'label' => __( 'Email Footer', 'formipay'),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
$notification_fields['notification_email_footer_links'] = [
'type' => 'repeater',
'label' => __( 'Icon Links', 'formipay' ),
'fields' => [
'type' => [
'type' => 'select',
'label' => __( 'Type', 'formipay' ),
'is_group_title' => true,
'options' => [
'dribbble' => 'Dribbble',
'facebook' => 'Facebook',
'facebook-messenger' => 'Messenger',
'github' => 'Github',
'instagram' => 'Instagram',
'link' => 'Link',
'linkedin' => 'Linkedin',
'pinterest' => 'Pinterest',
'telegram' => 'Telegram',
'tiktok' => 'Tiktok',
'whatsapp' => 'WhatsApp',
'wordpress' => 'WordPress',
'x' => 'X'
],
],
'link' => [
'type' => 'text',
'label' => __( 'URL', 'formipay' )
]
],
'group' => 'ended',
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty'
]
];
$fields = array_merge($fields, $notification_fields);
return $fields;
}
public function add_menu_on_global_setting($fields) {
$hints = formipay_editor_hints();
$statuses = formipay_order_status_list();
$medias = apply_filters('formipay/notification/media', ['email'] );
$notification_fields = [];
// access link
$notification_fields['notification_email_access_link_group'] = [
'type' => 'group_title',
'label' => __( 'Access Link', 'formipay' ),
'description' => __( 'This notification type is for responding request for access when people visit the thankyou page that contain any of your digital access (including files and URLs)', 'formipay' ),
'group' => 'started',
'submenu' => __( 'Access Link', 'formipay' ),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
$notification_fields['notification_email_access_link_title'] = [
'type' => 'text',
'label' => __( 'Title', 'formipay' ),
'value' => __( 'Your New Purchased Access Link', 'formipay' ),
'submenu' => __( 'Access Link', 'formipay' ),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
$notification_fields['notification_email_access_link_content'] = [
'type' => 'tinymce',
'label' => __( 'Content', 'formipay' ),
'hints' => [
'buyer_name' => __( 'Buyer Name', 'formipay' ),
'order_id' => __( 'Order ID', 'formipay' ),
'access_button' => __( 'Access Button', 'formipay' ),
'access_link' => __( 'Access Link', 'formipay' ),
],
'submenu' => __( 'Access Link', 'formipay' ),
'value' => '<p>Hello {{buyer_name}},</p><p><br></p><p>Anyone request to access your purchase order ID&nbsp;<span style="background-color: transparent;">{{order_id}}</span><span style="background-color: transparent;">. Here is the new access link:</span></p><p>{{access_button}}<br></p><p><br></p><p>Here is the raw link if there is issue with the button:</p><p>{{access_link}}</p><p><br></p><p>Do not give access to others to make sure your purcase safe. Please ignore this email if it was not you.</p><p><br></p><p>Best regards,</p><p>Your E-commerce Team</p>',
'group' => 'ended',
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
foreach($statuses as $key => $status){
$status_key = str_replace('-', '_', $key);
$notification_fields['notification_email_admin_'.$status_key.'_group_header'] = [
'type' => 'group_title',
// translators: %s is the status of order.
'label' => sprintf( __( 'Email Notification Admin (%s)', 'formipay' ), $status),
'group' => 'started',
'submenu' => esc_html($status),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
$notification_fields['notification_email_admin_'.$status_key.'_toggle'] = [
'type' => 'checkbox',
'label' => __('Activate', 'formipay'),
'submenu' => esc_html($status),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
$notification_fields['notification_email_admin_'.$status_key.'_title'] = [
'type' => 'text',
'label' => __( 'Title', 'formipay' ),
'submenu' => esc_html($status),
'dependency' => [
[
'key' => 'notification_email_admin_'.$status_key.'_toggle',
'value' => 'not_empty'
],
[
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
],
'dependencies' => '&&'
];
$notification_fields['notification_email_admin_'.$status_key.'_content'] = [
'type' => 'tinymce',
'label' => __( 'Content', 'formipay' ),
'hints' => [
'order_details' => __( 'Order Details', 'formipay' ),
'order_items' => __( 'Order Items', 'formipay' ),
'buyer_details' => __( 'Buyer Details', 'formipay' ),
'buyer_name' => __( 'Buyer Name', 'formipay' ),
'order_id' => __( 'Order ID', 'formipay' ),
'order_status' => __( 'Order Status', 'formipay' ),
'payment_timeout' => __( 'Payment Timeout', 'formipay' )
],
'submenu' => esc_html($status),
'dependency' => [
[
'key' => 'notification_email_admin_'.$status_key.'_toggle',
'value' => 'not_empty'
],
[
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
],
'dependencies' => '&&',
'group' => 'ended',
];
$notification_fields['notification_email_buyer_'.$status_key.'_group_header'] = [
'type' => 'group_title',
// translators: %s is the status of order.
'label' => sprintf( __( 'Email Notification Buyer (%s)', 'formipay' ), $status),
'group' => 'started',
'submenu' => esc_html($status),
'description' => __( 'Email notification for buyer only available if your form is contain email field type', 'formipay'),
];
$notification_fields['notification_email_buyer_'.$status_key.'_toggle'] = [
'type' => 'checkbox',
'label' => __('Activate', 'formipay'),
'submenu' => esc_html($status),
'dependency' => [
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
];
$notification_fields['notification_email_buyer_'.$status_key.'_title'] = [
'type' => 'text',
'label' => __( 'Title', 'formipay' ),
'submenu' => esc_html($status),
'dependency' => [
[
'key' => 'notification_email_buyer_'.$status_key.'_toggle',
'value' => 'not_empty'
],
[
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
],
'dependencies' => '&&'
];
$notification_fields['notification_email_buyer_'.$status_key.'_content'] = [
'type' => 'tinymce',
'label' => __( 'Content', 'formipay' ),
'hints' => [
'order_details' => __( 'Order Details', 'formipay' ),
'order_items' => __( 'Order Items', 'formipay' ),
'buyer_details' => __( 'Buyer Details', 'formipay' ),
'buyer_name' => __( 'Buyer Name', 'formipay' ),
'order_id' => __( 'Order ID', 'formipay' ),
'order_status' => __( 'Order Status', 'formipay' ),
'payment_timeout' => __( 'Payment Timeout', 'formipay' )
],
'submenu' => esc_html($status),
'dependency' => [
[
'key' => 'notification_email_buyer_'.$status_key.'_toggle',
'value' => 'not_empty'
],
[
'key' => 'notification_email_active',
'value' => 'not_empty',
'section' => 'General'
]
],
'dependencies' => '&&',
'group' => 'ended',
];
$notification_fields = apply_filters('formipay/settings/notification/fields/'.$status_key, $notification_fields);
}
$fields['notification'] = [
'name' => __('Notification', 'formipay'),
'fields' => $notification_fields
];
return $fields;
}
public function add_menu_on_product_setting($fields) {
$notification_fields = [];
$hints = formipay_editor_hints();
$statuses = formipay_order_status_list();
foreach($statuses as $key => $status){
$status_key = str_replace('-', '_', $key);
$notification_fields['product_notification_email_buyer_'.$status_key.'_group_header'] = [
'type' => 'group_title',
// translators: %s is the status of order.
'label' => sprintf( __( 'Email Notification Buyer (%s)', 'formipay' ), $status),
'group' => 'started',
'description' => __( 'Email notification for buyer only available if your form is contain email field type', 'formipay'),
];
$notification_fields['product_notification_email_buyer_'.$status_key.'_toggle'] = [
'type' => 'checkbox',
'label' => __('Replace Content', 'formipay'),
];
$notification_fields['product_notification_email_buyer_'.$status_key.'_content'] = [
'type' => 'tinymce',
'label' => __( 'Email Content', 'formipay' ),
'hints' => $hints,
'dependency' => [
'key' => 'product_notification_email_buyer_'.$status_key.'_toggle',
'value' => 'not_empty'
],
'group' => 'ended',
];
$notification_fields = apply_filters('formipay/product/notification/fields/', $notification_fields, $status_key);
}
$fields['formipay_form_settings']['notification'] = [
'name' => __('Notification', 'formipay'),
'fields' => $notification_fields
];
return $fields;
}
public function order_trigger($order_data) {
$medias = apply_filters('formipay/notification/media', ['email'] );
$order_status = $order_data['status'];
if(!empty($medias)){
foreach($medias as $media){
do_action(
'formipay/notification/order/'.$media, $order_data
);
}
}
}
public function access_trigger($order_data) {
$medias = apply_filters('formipay/notification/media', ['email'] );
if(!empty($medias)){
foreach($medias as $media){
do_action(
'formipay/notification/access/'.$media, $order_data
);
}
}
}
}

1259
includes/Order.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,725 @@
<?php
namespace Formipay\Payment;
use Formipay\Traits\SingletonTrait;
use Formipay\Order as Order;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class BankTransfer extends Payment {
use SingletonTrait;
private $gateway = 'bank_transfer';
protected function __construct() {
parent::__construct();
add_action( 'init', [$this, 'create_db'] );
add_action( 'formipay/order/new', [$this, 'add_transaction'], 99 );
add_action( 'formipay/order/payment-timeout', [$this, 'auto_cancel_order_on_timeout'] );
add_filter( 'formipay/order/order-details', [$this, 'add_unique_code_details'], 89, 3 );
add_filter( 'formipay/form-config/tab:payments/gateways', [$this, 'add_gateway'] );
add_filter( 'formipay/form-config/tab:payments', [$this, 'add_bank_transfer_settings'] );
add_filter( 'formipay/global-settings/tab:payment', [$this, 'add_settings'] );
add_filter( 'formipay/order/payment-details/bank_transfer', [$this, 'render_payment_details'], 999, 4 );
add_action('wp_ajax_formipay_bank_transfer_confirmation', [$this, 'handle_formipay_bank_transfer_confirmation'] );
add_action('wp_ajax_nopriv_formipay_bank_transfer_confirmation', [$this, 'handle_formipay_bank_transfer_confirmation'] );
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_bank_transfer_trx` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`created_date` datetime DEFAULT CURRENT_TIMESTAMP,
`form_id` int DEFAULT 0,
`order_id` int DEFAULT 0,
`channel` text NOT NULL,
`total` float(10, 2) DEFAULT 0,
`unique_code` int DEFAULT 0,
`meta_data` text,
PRIMARY KEY (`id`)
) $charset_collate;";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($create);
}
public function add_transaction($order_data) {
global $wpdb;
$formipay_settings = get_option('formipay_settings');
$timeout = isset($formipay_settings['bank_transfer_timeout']) ? (int) $formipay_settings['bank_transfer_timeout'] : 1440;
$table = $wpdb->prefix . 'formipay_bank_transfer_trx';
if ($order_data['payment_gateway'] == 'bank_transfer') {
$submit_args = [
'created_date' => formipay_date('Y-m-d H:i:s', strtotime($order_data['created_date'])),
'form_id' => intval($order_data['form_id']),
'order_id' => intval($order_data['id']),
'channel' => sanitize_text_field(str_replace($order_data['payment_gateway'].':::', '', $order_data['form_data']['payment'])),
'total' => floatval($order_data['total']),
'unique_code' => sanitize_text_field($this->check_unique_code()),
'meta_data' => maybe_serialize(array())
];
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
$inserted = $wpdb->insert($table, $submit_args);
wp_schedule_single_event(
(int) formipay_date('timestamp') + $timeout, 'formipay/order/payment-timeout', [
'order_id' => $order_data['id'],
'payment_gateway' => 'bank_transfer'
]
);
}
}
public function auto_cancel_order_on_timeout($order_id, $payment_gateway) {
if($payment_gateway == 'bank_transfer'){
global $wpdb;
$order = formipay_get_order($order_id);
if($order['status'] !== 'completed'){
Order::update($order_id, [
'status' => 'cancelled'
]);
}
$this->cancel_payment($order);
}
}
public function cancel_payment($order_data){
do_action('formipay/notification/order', $order_data);
}
public function check_unique_code() {
global $wpdb;
$table = $wpdb->prefix . 'formipay_bank_transfer_trx';
// Get the highest existing ID efficiently
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.NoCaching
$max_id = $wpdb->get_var("SELECT MAX(id) FROM $table");
$unique_code = !is_null($max_id) ? ($max_id + 1) : 1;
$operation = get_option('bank_transfer_unique_code_operation');
return ($operation == 'add') ? $unique_code : "-{$unique_code}";
}
public function add_unique_code_details($details, $form_id, $order_data){
if( floatval(formipay_get_post_meta($form_id, 'product_price')) > 0 &&
$order_data['payment_gateway'] == 'bank_transfer'
){
$details[] = [
'item' => __( 'Unique Code', 'formipay' ),
'amount' => $this->check_unique_code(),
'subtotal' => floatval($this->check_unique_code()),
'context' => floatval($this->check_unique_code()) < 0 ? 'sub' : 'add'
];
}
return $details;
}
public function add_gateway($gateways){
$formipay_settings = get_option('formipay_settings');
if(isset($formipay_settings[$this->gateway.'_toggle']) && false !== $formipay_settings[$this->gateway.'_toggle']){
if(isset($formipay_settings[ $this->gateway.'_channels' ]) && !empty($formipay_settings[ $this->gateway.'_channels' ])){
$channels = $formipay_settings[ $this->gateway.'_channels' ];
$channel_options = [];
if(!empty($channels)){
foreach($channels as $index => $channel){
$id = $this->gateway.':::'.$channel[$this->gateway.'_name'].'-'.$index;
$gateways[] = [
'id' => $id,
'gateway' => __( 'Bank', 'formipay' ),
'channel' => $channel[$this->gateway.'_name']
];
}
}
}
}
return $gateways;
}
public function add_bank_transfer_settings($fields){
$banktransfer_field = array(
$this->gateway.'_group' => array(
'type' => 'group_title',
'label' => __( 'Settings', 'formipay' ),
'description' => __( 'This will be shown in thank-you page and implemented when buyer uses one of Bank Transfer\'s channels', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'group' => 'started'
),
$this->gateway.'_text_section_intro' => array(
'type' => 'text',
'label' => __( 'Payment Details Intro', 'formipay' ),
'value' => __( 'Please complete payment to:', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' )
),
$this->gateway.'_confirmation_submit_button' => array(
'type' => 'text',
'label' => __( 'Payment Confirmation Submit Button Text', 'formipay' ),
'value' => __( 'Upload', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'group' => 'ended'
),
$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' => __( 'Bank Transfer', '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' => __( 'Bank Transfer', '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' => __( 'Bank Transfer', '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' => __( 'Bank Transfer', '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' => __( 'Bank Transfer', 'formipay' ),
'group' => 'ended'
)
);
foreach($banktransfer_field as $key => $value){
$fields[$key] = $value;
}
return $fields;
}
public function add_settings($settings) {
$bank_transfer = array(
'my_code_field' => [
'type' => 'ace_editor',
'label' => __('Custom Code', 'formipay'),
'lang' => 'javascript', // or 'css', 'html', etc.
'theme' => 'chrome',
'value' => '', // default value
'required' => true,
],
$this->gateway.'_toggle' => array(
'type' => 'checkbox',
'label' => __('Activate Bank Transfer', 'formipay'),
'submenu' => __( 'Bank Transfer', 'formipay' )
),
$this->gateway.'_general_group' => array(
'type' => 'group_title',
'label' => __( 'General', 'formipay' ),
'group' => 'started',
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'submenu' => __( 'Bank Transfer', 'formipay' )
),
$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',
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'submenu' => __( 'Bank Transfer', 'formipay' )
),
$this->gateway.'_unique_codes' => array(
'type' => 'repeater',
'label' => esc_html__( 'Unique Codes', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'description' => __( 'Get unique total amount by adding unique code.', 'formipay' ),
'fields' => array(
'unique_code_currency' => array(
'type' => 'select',
'label' => __( 'Currency', 'formipay' ),
'description' => __( 'This setting is only applied when the selected currency matches the form currency.', 'formipay' ),
'options' => formipay_currency_as_options(),
'is_group_title' => true,
'searchable' => true,
'required' => true
),
'unique_code_toggle' => array(
'type' => 'checkbox',
'label' => __( 'Activate', 'formipay' ),
'description' => __( 'Generate unique code for this currency.', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
),
'unique_code_increment_step' => array(
'type' => 'number',
'label' => esc_html__( 'Increment Step', 'formipay' ),
'value' => 0.01,
'step' => 0.001,
'required' => true
),
'unique_code_max' => array(
'type' => 'number',
'label' => esc_html__( 'Max Amount', 'formipay' ),
'value' => 0.01,
'step' => 0.001,
'required' => true
),
'unique_code_operation' => array(
'type' => 'select',
'label' => __('Operation', 'formipay'),
'options' => array(
'sub' => __( '(-) Decrease Total Amount by Unique Code', 'formipay' ),
'add' => __( '(+) Increase Total Amount by Unique Code', 'formipay' ),
),
'value' => 'add',
'dependency'=> array(
array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
array(
'key' => $this->gateway.'_unique_code_toggle',
'value' => 'not_empty'
),
),
'dependencies' => '&&',
'required' => true,
),
)
),
$this->gateway.'_confirmation_group' => array(
'type' => 'group_title',
'label' => __( 'Confirmation Form', 'formipay' ),
'description' => __( 'for order that use Bank Transfer gateway only.', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => 'bank_transfer_toggle',
'value' => 'not_empty',
),
'group' => 'started'
),
$this->gateway.'_confirmation_dropzone_color' => array(
'type' => 'color',
'label' => __( 'Dropzone Box Background color', 'formipay' ),
'value' => '#cccccc',
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => 'bank_transfer_toggle',
'value' => 'not_empty',
),
),
$this->gateway.'_confirmation_dropzone_text_color' => array(
'type' => 'color',
'label' => __( 'Dropzone Box Text Color', 'formipay' ),
'value' => '#808080',
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => 'bank_transfer_toggle',
'value' => 'not_empty',
),
),
$this->gateway.'_confirmation_dropzone_instruction' => array(
'type' => 'text',
'label' => __( 'Dropzone Instruction', 'formipay' ),
'value' => __( 'Drag & drop a file here or click to select one', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => 'bank_transfer_toggle',
'value' => 'not_empty',
),
),
$this->gateway.'_confirmation_submit_button' => array(
'type' => 'text',
'label' => __( 'Submit Button', 'formipay' ),
'value' => __( 'Upload', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => 'bank_transfer_toggle',
'value' => 'not_empty',
),
'group' => 'ended'
),
$this->gateway.'_settings_group' => array(
'type' => 'group_title',
'label' => __( 'Account List', 'formipay' ),
'description' => __( 'Add all of your preferred bank accounts to receive the payment', 'formipay' ),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'started'
),
$this->gateway.'_channels' => array(
'type' => 'repeater',
'label' => __('Bank Accounts', 'formipay'),
'fields' => array(
$this->gateway.'_name' => array(
'type' => 'text',
'label' => __( 'Bank Name', 'formipay' ),
'required' => true,
'is_group_title' => true
),
$this->gateway.'_account' => array(
'type' => 'text',
'label' => __( 'Bank Account', 'formipay' ),
'required' => true
),
$this->gateway.'_account_holder' => array(
'type' => 'text',
'label' => __( 'Bank Account Holder', 'formipay' ),
'required' => true
),
$this->gateway.'_logo' => array(
'type' => 'image',
'label' => __( 'Bank Logo', 'formipay' ),
'required' => true
),
),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'submenu' => __( 'Bank Transfer', 'formipay' ),
'group' => 'ended'
)
);
foreach($bank_transfer as $key => $value){
$settings[$key] = $value;
}
return $settings;
}
public function render_payment_details($render, $form_id, $order_data, $submit_action_type) {
if(in_array($order_data['status'], ['completed', 'payment-confirm'])){
return $render;
}
$get_order = formipay_get_order($order_data['id']);
$selected_payment = ''; // bank_transfer:::BNI-0
$payment_gateway = '';
if(!empty($get_order['form_data'])){
foreach($get_order['form_data'] as $_form_data){
if($_form_data['name'] == 'payment') {
$selected_payment = $_form_data['value'];
}
if($_form_data['name'] == 'payment_gateway') {
$payment_gateway = $_form_data['value'];
}
}
}
if('' !== $selected_payment){
$formipay_settings = get_option('formipay_settings');
if($formipay_settings[$payment_gateway.'_toggle'] !== false){
$gateway_channel = [];
if(!empty($formipay_settings[$payment_gateway.'_channels'])){
foreach($formipay_settings[$payment_gateway.'_channels'] as $channel){
if(strpos($selected_payment, $channel[$payment_gateway.'_name']) !== false){
$gateway_channel = $channel;
break;
}
}
}
if(!empty($gateway_channel)){
if($submit_action_type == 'thankyou'){
ob_start();
?>
<span><?php echo esc_html(formipay_get_post_meta($form_id, 'bank_transfer_text_section_intro')); ?></span>
<?php
if(!empty($gateway_channel['bank_transfer_logo'])){
echo '<br>'.wp_get_attachment_image( $gateway_channel['bank_transfer_logo'], 'thumbnail', false );
}
?>
<div class="payment-bank_transfer payment-response-container">
<table id="payment-details">
<tbody>
<tr>
<th><?php echo esc_html__( 'Order ID', 'formipay' ); ?></th>
<td><?php echo '#'.intval($order_data['id']); ?></td>
</tr>
<tr>
<th><?php echo esc_html__('Bank Name', 'formipay'); ?></th>
<td><?php echo esc_html($gateway_channel['bank_transfer_name']) ?></td>
</tr>
<tr>
<th><?php echo esc_html__('Bank Account', 'formipay'); ?></th>
<td>
<p class="bank-account" data-copy-value="<?php echo esc_attr($gateway_channel['bank_transfer_account']) ?>">
<b><?php echo esc_html($gateway_channel['bank_transfer_account']) ?></b>
</p>
<button class="formipay-copy-button"
data-copy-text="<?php echo esc_attr__( 'Copy', 'formipay' ); ?>"
data-copied-text="<?php echo esc_attr__( 'Copied', 'formipay' ); ?>"
data-not-copied-text="<?php echo esc_attr__( 'Not copied!', 'formipay' ); ?>"
>
<i class="bi bi-copy"></i> <?php echo esc_html__( 'Copy', 'formipay' ); ?>
</button>
</td>
</tr>
<tr>
<th><?php echo esc_html__('Bank Account Holder', 'formipay'); ?></th>
<td><?php echo esc_html($gateway_channel['bank_transfer_account_holder']); ?></td>
</tr>
<tr>
<th><?php echo esc_html__('Payment Total', 'formipay'); ?></th>
<td>
<p class="payment-total" data-copy-value="<?php echo esc_attr($order_data['total']) ?>">
<b><?php echo esc_html(formipay_price_format($order_data['total'], $form_id)); ?></b>
</p>
<button class="formipay-copy-button"
data-copy-text="<?php echo esc_attr__( 'Copy', 'formipay' ); ?>"
data-copied-text="<?php echo esc_attr__( 'Copied', 'formipay' ); ?>"
data-not-copied-text="<?php echo esc_attr__( 'Not copied!', 'formipay' ); ?>"
>
<i class="bi bi-copy"></i> <?php echo esc_html__( 'Copy', 'formipay' ); ?>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<form id="uploadForm" class="dropzone">
<input type="hidden" name="action" value="formipay_bank_transfer_confirmation">
<input type="hidden" name="order_id" value="<?php echo esc_attr($order_data['id']); ?>">
<input type="hidden" name="form_id" value="<?php echo esc_attr($order_data['form_id']); ?>">
<input type="hidden" name="total_amount" value="<?php echo esc_attr($order_data['total']); ?>">
<input type="hidden" name="nonce" value="<?php echo esc_attr(wp_create_nonce('formipay_bank_transfer_confirmation')); ?>">
<div class="dropzone-area" id="dropzoneArea">
<input type="file" name="file" id="fileInput" style="display: none;" accept="image/jpeg, image/png" />
<div id="thumbnailPreview" style="display: none;">
<?php // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- This image is a placeholder and will be managed by JS. ?>
<img id="previewImage" src="" alt="Image Preview"/>
</div>
<i class="bi bi-image"></i>
<div><?php echo esc_html($formipay_settings['bank_transfer_confirmation_dropzone_instruction']); ?></div>
</div>
<button type="submit" id="upload-transfer-receipt" class="formipay-submit-button"><?php echo esc_html(formipay_get_post_meta($form_id, 'bank_transfer_confirmation_submit_button')); ?></button>
</form>
<?php
$render = ob_get_clean();
}elseif($submit_action_type == 'whatsapp'){
$slug = 'thankyou';
if(!empty($formipay_settings['thankyou_link'])){
$slug = $formipay_settings['thankyou_link'];
}
// translators: %d is the order ID.
$message = sprintf( esc_html__( 'Order ID: *%d*', 'formipay'), $order_data['id'] );
// translators: %s is the chosen bank name.
$message .= "%0A".sprintf( esc_html__( 'Bank Name: *%s*', 'formipay'), $gateway_channel['bank_transfer_name'] );
// translators: %s is the chosen bank account.
$message .= "%0A".sprintf( esc_html__( 'Bank Account: *%s*', 'formipay'), $gateway_channel['bank_transfer_account'] );
// translators: %s is the chosen bank account holder name.
$message .= "%0A".sprintf( esc_html__( 'Bank Account Holder: *%s*', 'formipay'), $gateway_channel['bank_transfer_account_holder'] );
// translators: %s is the total amount of order.
$message .= "%0A".sprintf( esc_html__( 'Payment Total: *%s*', 'formipay'), formipay_price_format($order_data['total'], $form_id) );
// translators: %s is the payment confirmation URL.
$message .= "%0A%0A> ".sprintf( esc_html__( 'Upload your transfer receipt to confirm the payment to %s', 'formipay' ), site_url('/'.$slug.'/'.base64_encode($form_id.':::'.$order_data['id'].':::'.$order_data['meta_data']['session_id'])));
$render = $message;
}
}
}
}
return $render;
}
public function handle_formipay_bank_transfer_confirmation() {
// Capability check
// if (!current_user_can('edit_posts')) {
// wp_send_json_error(['message' => __('Permission denied', 'formipay')]);
// exit;
// }
// Nonce verification
if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'formipay_bank_transfer_confirmation')) {
wp_send_json_error(['message' => __('Nonce verification failed', 'formipay')]);
exit;
}
// Parameter sanitization
$form_id = isset($_POST['form_id']) ? sanitize_text_field(wp_unslash($_POST['form_id'])) : '';
$order_id = isset($_POST['order_id']) ? sanitize_text_field(wp_unslash($_POST['order_id'])) : '';
// File validation
if (empty($_FILES['file']['tmp_name']) || !is_uploaded_file(sanitize_text_field($_FILES['file']['tmp_name']))) {
wp_send_json_error(['message' => __('Invalid file upload', 'formipay')]);
exit;
}
// Check for errors
if (isset($_FILES['file']['error']) && $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
wp_send_json_error(array('message' => __('File upload error', 'formipay')));
exit;
}
// Require the WordPress file system
require_once(ABSPATH . 'wp-admin/includes/file.php');
// Use wp_handle_upload() to save the file
$movefile = wp_handle_upload($_FILES['file'], array('test_form' => false));
if ($movefile && !isset($movefile['error'])) {
// File is uploaded successfully; now save it to media library
$file_path = $movefile['file'];
$file_type = wp_check_filetype(basename($file_path), null);
$attachment = array(
'post_mime_type' => $file_type['type'],
'post_title' => 'receipt-order-'.$order_id.'-'.sanitize_file_name(basename($file_path)),
'post_content' => '',
'post_status' => 'inherit'
);
// Insert the attachment in the media library
$attach_id = wp_insert_attachment($attachment, $file_path);
require_once(ABSPATH . 'wp-admin/includes/image.php');
$attach_data = wp_generate_attachment_metadata($attach_id, $file_path);
wp_update_attachment_metadata($attach_id, $attach_data);
$update = formipay_update_order_status([
'form_id' => $form_id,
'order_id' => $order_id,
'status' => 'payment-confirm',
'payment_gateway' => 'bank_transfer'
]);
if($update['valid']){
// Edit TRX DB
$meta_data = [
'transfer_receipt' => [
'time' => time(),
'attachment_id' => $attach_id,
'attachment_url' => $movefile['url']
]
];
global $wpdb;
$trx_db = $wpdb->prefix.'formipay_bank_transfer_trx';
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$wpdb->update( $trx_db, ['meta_data' => maybe_serialize($meta_data)], ['order_id' => $order_id] );
}else{
wp_delete_attachment( $attach_id, true );
}
wp_send_json( [
'success' => boolval($update['valid']),
'data' => [
'message' => $update['message'],
'icon' => (boolval($update['valid']) == true) ? 'success' : 'info'
]
] );
}
// Send an error response back
wp_send_json_error([
'message' => 'File could not be saved. Error: ' . $movefile['error']
]);
}
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');
$slug = 'thankyou';
if(!empty($formipay_settings['thankyou_link'])){
$slug = $formipay_settings['thankyou_link'];
}
$button = [
'url' => site_url('/'.$slug.'/'.base64_encode($order_data['form_id'].':::'.$order_data['id'])),
'label' => formipay_get_post_meta($order_data['form_id'], 'bank_transfer_confirmation_submit_button')
];
}
return $button;
}
public function process_payment($order_data) {
return $order_data;
}
}

View File

@@ -0,0 +1,581 @@
<?php
namespace Formipay\Payment;
use Formipay\Traits\SingletonTrait;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class CashOnDelivery extends Payment {
use SingletonTrait;
private $gateway = 'cod';
protected function __construct() {
parent::__construct();
add_filter( 'formipay/global-settings/tab:payment', [$this, 'add_settings'] );
add_filter( 'formipay/form-config/tab:payments', [$this, 'add_cod_setings'] );
add_filter( 'formipay/form-config/tab:payments/gateways', [$this, 'add_gateway'] );
add_filter( 'formipay/payment-list/cod/logo', [$this, 'set_frontend_logo'] );
add_filter( 'formipay/order/order-details', [$this, 'add_fee'], 999, 3 );
add_filter( 'formipay/order/payment-details/cod', [$this, 'render_payment_details'], 999, 4 );
add_action( 'template_redirect', [$this, 'check_parse_query'], 999 );
add_filter( 'formipay/thankyou/shortcodes', [$this, 'add_order_shortcodes'], 999, 4 );
add_action('wp_ajax_formipay_cod_confirmation', [$this, 'handle_formipay_cod_confirmation'] );
add_action('wp_ajax_nopriv_formipay_cod_confirmation', [$this, 'handle_formipay_cod_confirmation'] );
}
public function add_transaction($order_data){
return;
}
public function cancel_payment($order_id) {
return;
}
public function add_settings($settings) {
$cod = array(
$this->gateway.'_toggle' => array(
'type' => 'checkbox',
'label' => __('Activate Cash On Delivery', 'formipay'),
'submenu' => __( 'Cash On Delivery', 'formipay' )
),
$this->gateway.'_settings_group' => array(
'type' => 'group_title',
'label' => __( 'General', 'formipay' ),
'description' => __( 'Set the rule of Cash On Delivery', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'started'
),
$this->gateway.'_label' => array(
'type' => 'text',
'label' => __('Label', 'formipay'),
'value' => __( 'COD', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_item_name' => array(
'type' => 'text',
'label' => __('Item Name', 'formipay'),
'value' => __( 'COD Fee', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_icon' => array(
'type' => 'image',
'label' => __('Icon', 'formipay'),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_fee_amount' => array(
'type' => 'number',
'label' => __('Fee Amount', 'formipay'),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_fee_type' => array(
'type' => 'select',
'label' => __('Fee Type', 'formipay'),
'options' => array(
'fixed' => __( 'Fixed Amount', 'formipay' ),
'percentage' => __( 'Percentage', 'formipay' )
),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'ended'
),
$this->gateway.'_details_group' => array(
'type' => 'group_title',
'label' => __( 'Instruction', 'formipay' ),
'description' => __( 'Set instruction to buyer how to pay with COD method.', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'started'
),
$this->gateway.'_instruction_thankyou' => array(
'type' => 'tinymce',
'label' => __( 'Thankyou Screen', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'hints' => array(
'product_name' => __( 'Product Name', 'formipay' ),
'order_id' => __( 'Order ID', 'formipay' ),
'grand_total' => __( 'Order Total', 'formipay' ),
'confirmation_page_link' => __( 'Confirmation Page Link', 'formipay' )
),
'value' => '<p>Please prepare your cash of <span style="text-align: var(--bs-body-text-align);">{{grand_total}}&nbsp;</span><span style="text-align: var(--bs-body-text-align);">and pay it to the courier who delivers your&nbsp;</span>{{product_name}}.</p>',
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
),
$this->gateway.'_instruction_whatsapp' => array(
'type' => 'hint_textarea',
'label' => __( 'WhatsApp Message', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'hints' => array(
'product_name' => __( 'Product Name', 'formipay' ),
'order_id' => __( 'Order ID', 'formipay' ),
'grand_total' => __( 'Order Total', 'formipay' ),
'confirmation_page_link' => __( 'Confirmation Page Link', 'formipay' )
),
'value' => 'Please prepare your cash of {{grand_total}} and pay it to the courier who delivers your {{product_name}}.',
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'ended'
),
$this->gateway.'_confirmation_page_group' => array(
'type' => 'group_title',
'label' => __( 'Confirmation Page', 'formipay' ),
'description' => sprintf(
// translators: %s is the site URL.
__(
'<p>Set confirmation page for buyer to confirm that they have receive the package and pay the order to courier.</p>
<p>Confirmation magic URL: <code>%s/payment-confirm/cod/{{autogenerated_token}}</code></p>'
, 'formipay' ), site_url()),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'started'
),
$this->gateway.'_confirmation_page_content' => array(
'type' => 'tinymce',
'label' => __( 'Page Content', 'formipay' ),
'hints' => array(
'order_details' => __( 'Order Details', 'formipay' ),
'confirmation_form' => __( 'Confirmation Form', 'formipay' ),
),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_confirmation_page_width' => array(
'type' => 'number',
'label' => __( 'Page Width', 'formipay' ),
'value' => '600',
'description' => __( 'in pixel (px)', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_confirmation_table_alignment' => array(
'type' => 'radio',
'label' => __('Table Content Alignment', 'formipay'),
'value' => 'center',
'options' => array(
'left' => '<i class="bi bi-text-left"></i> Left',
'center' => '<i class="bi bi-text-center"></i> Center',
'right' => '<i class="bi bi-text-right"></i> Right',
),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_confirmation_form_agreement' => array(
'type' => 'text',
'label' => __( 'Agreement', 'formipay' ),
'value' => __( 'I have receipt the package and pay as billed to the courier.', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
)
),
$this->gateway.'_confirmation_form_button' => array(
'type' => 'text',
'label' => __( 'Button Text', 'formipay' ),
'value' => __( 'Confirm', 'formipay' ),
'submenu' => __( 'Cash On Delivery', 'formipay' ),
'dependency' => array(
'key' => $this->gateway.'_toggle',
'value' => 'not_empty'
),
'group' => 'ended',
)
);
foreach($cod as $key => $value){
$settings[$key] = $value;
}
return $settings;
}
public function add_cod_setings($fields) {
$cod_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' => __( 'COD', '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' => __( 'COD', '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 COD #{{order_id}}', 'formipay'),
'description' => __('When successfully change the order status to the preferred one', 'formipay'),
'submenu' => __( 'COD', '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' => __( 'COD', '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' => __( 'COD', 'formipay' ),
'group' => 'ended'
)
);
foreach($cod_settings as $key => $value){
$fields[$key] = $value;
}
return $fields;
}
public function add_gateway($gateways) {
$formipay_settings = get_option('formipay_settings');
if(isset($formipay_settings['cod_toggle']) && false !== $formipay_settings['cod_toggle']){
// $gateways['cod'] = __('Cash On Delivery', 'formipay');
$gateways[] = [
'id' => $this->gateway,
'gateway' => __( 'COD', 'formipay' ),
];
}
return $gateways;
}
public function set_frontend_logo($logo){
if($logo == false){
$logo = FORMIPAY_URL . 'public/assets/img/cod_default_icon.png';
}
return $logo;
}
public function add_fee($details, $form_id, $order_data){
if($order_data['payment_gateway'] == 'cod'){
$formipay_settings = get_option('formipay_settings');
$fee_amount = $formipay_settings['cod_fee_amount'];
$fee_type = $formipay_settings['cod_fee_type'];
if($fee_type == 'percentage'){
$grand_total = 0;
if(!empty($details)){
foreach($details as $detail){
$grand_total += $detail['subtotal'];
}
}
$fee_amount = $grand_total * floatval($fee_amount) / 100;
}
$details[] = [
'item' => $formipay_settings['cod_item_name'],
'amount' => $fee_amount,
'subtotal' => $fee_amount
];
}
return $details;
}
public function render_payment_details($render, $form_id, $order_data, $submit_action_type) {
if($order_data['payment_gateway'] == 'cod'){
$render = '';
$formipay_settings = get_option('formipay_settings');
$render .= ($submit_action_type == 'thankyou') ? '<div class="cod-instruction" style="text-align:'.formipay_get_post_meta($form_id, 'thankyou_screen_table_alignment').';">' : '';
$replacements = [
'{{product_name}}' => get_the_title($form_id),
'{{order_id}}' => $order_data['id'],
'{{grand_total}}' => formipay_price_format($order_data['total'], $form_id),
'{{confirmation_page_link}}' => site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($form_id.':::'.$order_data['id']))
];
$render .= strtr( $formipay_settings['cod_instruction_'.$submit_action_type], $replacements );
$render .= ($submit_action_type == 'thankyou') ? '</div>' : '';
}
return $render;
}
public function check_parse_query() {
global $wp_query;
$formipay_settings = get_option('formipay_settings');
if(is_admin()) :
return;
endif;
if(
!is_admin() &&
is_array($wp_query->query) &&
array_key_exists('formipay-payment-confirm', $wp_query->query) &&
true === boolval($wp_query->query['formipay-payment-confirm']) &&
array_key_exists('gateway', $wp_query->query) &&
'cod' === $wp_query->query['gateway']
) :
$token = explode(':::', base64_decode($wp_query->query['formipay-token']));
$form_id = $token[0];
$order_id = $token[1];
$thankyou_slug = 'thankyou';
if(!empty($formipay_settings['thankyou_link'])){
$thankyou_slug = $formipay_settings['thankyou_link'];
}
$order = formipay_get_order($order_id);
if($order['payment_gateway'] !== 'cod'){
wp_safe_redirect( site_url('/'.$thankyou_slug.'/'.$wp_query->query['formipay-token']) );
}
$button_background_color = json_decode(formipay_get_post_meta($form_id, 'button_bg_color'), true );
$button_text_color = json_decode(formipay_get_post_meta($form_id, 'button_text_color'), true );
$button_border_color = json_decode( formipay_get_post_meta($form_id, 'button_border_color'), true );
get_header();
?>
<style>
body {
--formipay-confirmation-page-width: <?php echo esc_html($formipay_settings[$this->gateway.'_confirmation_page_width']); ?>px;
--formipay-confirmation-page-table-alignment: <?php echo esc_html($formipay_settings[$this->gateway.'_confirmation_table_alignment']) ?>;
--formipay-button-submit-bg-color: <?php echo esc_html($button_background_color['regular']) ?? '#000000'; ?>;
--formipay-button-submit-bg-color-hover: <?php echo esc_html($button_background_color['hover']) ?? '#ffffff'; ?>;
--formipay-button-submit-bg-color-active: <?php echo esc_html($button_background_color['active']) ?? '#ffffff'; ?>;
--formipay-button-submit-text-color: <?php echo esc_html($button_text_color['regular']) ?? '#ffffff'; ?>;
--formipay-button-submit-text-color-hover: <?php echo esc_html($button_text_color['hover']) ?? '#000000'; ?>;
--formipay-button-submit-text-color-active: <?php echo esc_html($button_text_color['active']) ?? '#000000'; ?>;
--formipay-button-submit-border-color: <?php echo esc_html($button_border_color['regular']) ?? '#000000;' ?>;
--formipay-button-submit-border-color-hover: <?php echo esc_html($button_border_color['hover']) ?? '#ffffff'; ?>;
--formipay-button-submit-border-color-active: <?php echo esc_html($button_border_color['active']) ?? '#ffffff'; ?>;
}
</style>
<div class="formipay-container">
<div class="formipay-content-wrapper">
<?php
echo wp_kses_post(str_replace(
[
'{{order_details}}',
'{{confirmation_form}}'
],
[
$this->render_order_details_table($form_id, $order_id),
$this->render_confirmation_form($form_id, $order_id)
],
$formipay_settings[$this->gateway.'_confirmation_page_content']
))
?>
</div>
</div>
<?php
get_footer();
exit;
endif;
}
public function render_confirmation_form($form_id, $order_id){
$formipay_settings = get_option( 'formipay_settings' );
$order = formipay_get_order($order_id);
ob_start();
if($order['status'] == 'on-hold') {
?>
<form id="formipay-cod-confirmation">
<input type="hidden" id="form_id" value="<?php echo intval($form_id); ?>">
<input type="hidden" id="order_id" value="<?php echo intval($order_id); ?>">
<?php wp_nonce_field('formipay_cod_confirmation', 'formipay_nonce'); ?>
<div class="formipay-agreement">
<input id="cod_agreement" type="checkbox" class="formipay-input">
<label for="cod_agreement"><?php echo esc_html($formipay_settings['cod_confirmation_form_agreement']); ?></label>
</div>
<div class="formipay-button-wrapper">
<button type="button" id="confirm-cod-order" class="formipay-submit-button" disabled><?php echo esc_html($formipay_settings['cod_confirmation_form_button']); ?></button>
</div>
</form>
<?php
}
$content = ob_get_contents();
ob_end_clean();
return $content;
}
public function render_order_details_table($form_id, $order_id) {
ob_start();
?>
<table id="order-details">
<tbody>
<?php
$order = formipay_get_order($order_id);
if(false !== $order){
$items = maybe_unserialize($order['items']);
foreach($items as $item){
$label = ucwords(str_replace('_', ' ', $item['item']));
if(!in_array($label, ['payment', 'payment_gateway'])){
$qty = '';
if(isset($item['qty']) && !empty($item['qty'])){
$qty = ' × '.$item['qty'];
}
?>
<tr>
<th><?php echo esc_html($label) . esc_html($qty); ?></th>
<td><?php echo esc_html(formipay_price_format($item['subtotal'], $form_id)); ?></td>
</tr>
<?php
}
}
}
?>
<tr class="formipay-grand-total-row">
<th><?php echo esc_html__( 'Total', 'formipay' ); ?></th>
<td><?php echo esc_html(formipay_price_format($order['total'], $form_id)); ?></td>
</tr>
</tbody>
</table>
<?php
$content = ob_get_contents();
ob_end_clean();
return $content;
}
public function handle_formipay_cod_confirmation() {
// Check nonce
$nonce_valid = check_ajax_referer('formipay_cod_confirmation', 'formipay_nonce', false);
// Handle the valid nonce case
if ($nonce_valid) {
// Retrieve the data
$form_id = isset($_POST['form_id']) ? sanitize_text_field( wp_unslash($_POST['form_id']) ) : '';
$order_id = isset($_POST['order_id']) ? sanitize_text_field( wp_unslash($_POST['order_id']) ) : '';
$update = formipay_update_order_status([
'form_id' => $form_id,
'order_id' => $order_id,
'status' => 'payment-confirm',
'payment_gateway' => 'cod'
]);
wp_send_json( [
'success' => boolval($update['valid']),
'data' => [
'message' => $update['message'],
'icon' => (boolval($update['valid']) == true) ? 'success' : 'info'
]
] );
}
// Nonce is invalid, handle the error
wp_send_json_error([
'message' => 'Security check failed. Please try again.',
'icon' => 'error'
]);
}
public function add_order_shortcodes($shortcodes, $form_id, $order_data, $submit_action_type) {
if($order_data['payment_gateway'] == $this->gateway){
$confirmation_page_link = site_url('/payment-confirm/'.$this->gateway.'/'.base64_encode($form_id.':::'.$order_data['id']));
$shortcodes['payment_confirmation_form'] = '<a href="'.$confirmation_page_link.'" target="_blank">C.O.D Confirmation</a>';
}
return $shortcodes;
}
public function process_payment($order_data) {
return $order_data;
}
}

View File

@@ -0,0 +1,289 @@
<?php
namespace Formipay\Payment;
use Formipay\Traits\SingletonTrait;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
abstract class Payment {
use SingletonTrait;
abstract public function add_gateway($gateways);
abstract public function add_transaction($order_data);
abstract public function process_payment($order_data);
abstract public function cancel_payment($order_data);
protected function __construct() {
add_filter( 'formipay/global-settings', [$this, 'add_setting_payment_menu'], 20 );
add_filter( 'formipay/form-config', [$this, 'add_form_payment_menu'], 79 );
add_filter( 'formipay/form-config/tab:payments', [$this, 'add_responsive_grid'], 1012 );
add_filter( 'formipay/frontend/payment-list', [$this, 'add_payment_list'], 999, 2 );
add_filter( 'formipay/order/shortcodes', [$this, 'add_order_shortcodes'], 999, 4 );
add_filter( 'formipay/thankyou/shortcodes', [$this, 'add_order_shortcodes'], 999, 4 );
add_filter( 'formipay/order/process-data', [$this, 'add_payment_in_process_data'], 999, 2 );
add_filter( 'formipay/order/payment-details', [$this, 'render_payment_details'], 999, 4 );
add_action( 'init', [$this, 'set_endpoint'], 999);
add_filter( 'query_vars', [$this, 'set_query_vars'], 899);
add_action( 'wp_enqueue_scripts', [$this, 'frontend_enqueue'] );
add_filter( 'pre_get_document_title', [$this, 'payment_confirm_page_title'], 1000 );
}
public function add_setting_payment_menu($fields){
$general_payment_settings = array();
$payment_settings = apply_filters( 'formipay/global-settings/tab:payment', $general_payment_settings );
if(!empty($payment_settings)){
$fields['payment'] = array(
'name' => __('Payment', 'formipay'),
'fields' => $payment_settings
);
}
return $fields;
}
public function add_form_payment_menu($fields) {
$gateways = apply_filters( 'formipay/form-config/tab:payments/gateways', [] );
$payment_options = [];
if(!empty($gateways)){
foreach($gateways as $gateway){
$id = $gateway['id'];
$label = $gateway['gateway'];
if(isset($gateway['channel'])){
$label .= ' - '.$gateway['channel'];
}
$payment_options[] = [
'id' => $id,
'label' => $label
];
}
}
$payment_fields = [
'payment_section_group' => array(
'type' => 'group_title',
'label' => __( 'Payment Section', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'group' => 'started'
),
'payment_section_title' => array(
'type' => 'text',
'label' => __( 'Payment Section Title', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'value' => 'Payment Methods',
),
'payment_gateways' => array(
'type' => 'sorter',
'label' => __( 'Choose one or any', 'formipay' ),
'options' => array(
array(
'id' => 'inactive',
'name' => __( 'Inactive', 'formipay' ),
'options' => $payment_options,
),
array(
'id' => 'active',
'name' => __( 'Active', 'formipay' ),
'options' => [],
)
),
'submenu' => __( 'General', 'formipay' ),
),
'payment_label' => array(
'type' => 'checkbox',
'label' => __( 'Show Payment Label', 'formipay' ),
'description' => __( 'Show payment label in the payment options on the frontend.', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'group' => 'ended'
)
];
$payment_fields = apply_filters( 'formipay/form-config/tab:payments', $payment_fields );
$fields['formipay_form_settings']['payments'] = array(
'name' => __( 'Payments', 'formipay' ),
'fields' => $payment_fields
);
return $fields;
}
public function add_responsive_grid($channels) {
$channels['payment_responsive_box'] = array(
'type' => 'group_title',
'label' => __( 'Responsive', 'formipay' ),
'description' => __( 'Set responsive grid payment card options' , 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'group' => 'started',
);
$channels['payment_desktop_columns'] = array(
'type' => 'number',
'label' => __( 'Desktop View', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'value' => 4
);
$channels['payment_tablet_columns'] = array(
'type' => 'number',
'label' => __( 'Tablet View', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'value' => 3
);
$channels['payment_mobile_columns'] = array(
'type' => 'number',
'label' => __( 'Mobile View', 'formipay' ),
'submenu' => __( 'General', 'formipay' ),
'value' => 2,
'group' => 'ended'
);
return $channels;
}
public function add_payment_list($payment_list, $form_id) {
$formipay_settings = get_option('formipay_settings');
$payment_gateways = json_decode(formipay_get_post_meta($form_id, 'payment_gateways'), true);
foreach($payment_gateways as $payment){
if($payment['id'] == 'active' && !empty($payment['options'])){
foreach($payment['options'] as $option){
$id = $option['id'];
$_pay = explode(':::', $id);
$gateway = $_pay[0];
if(formipay_get_post_meta($form_id, 'product_type') == 'digital' && $gateway == 'cod'){
continue;
}
if(isset($formipay_settings[$gateway.'_channels']) && isset($_pay[1])){
$channel = $_pay[1];
if(strpos($channel, '-')){
$channel = explode('-', $channel);
$channel_index = $channel[1];
$payment_list[$id] = $formipay_settings[$gateway.'_channels'][$channel_index];
}
}else{
$label = $icon = '';
if(isset($formipay_settings[$gateway.'_label']) && !empty($formipay_settings[$gateway.'_label'])){
$label = $formipay_settings[$gateway.'_label'];
}
if(isset($formipay_settings[$gateway.'_icon']) && !empty($formipay_settings[$gateway.'_icon'])){
$icon = $formipay_settings[$gateway.'_icon'];
}
$payment_list[$id] = array(
$gateway.'_name' => apply_filters( 'formipay/payment-list/'.$gateway.'/label', $label),
$gateway.'_logo' => apply_filters( 'formipay/payment-list/'.$gateway.'/logo', $icon)
);
}
}
}
}
return $payment_list;
}
public function add_order_shortcodes($shortcodes, $form_id, $order_data, $submit_action_type) {
$shortcodes['payment_details'] = apply_filters( 'formipay/order/payment-details', '', $form_id, $order_data, $submit_action_type );
return $shortcodes;
}
public function render_payment_details($render, $form_id, $order_data, $submit_action_type) {
$payment_gateway = $order_data['payment_gateway'];
$render = apply_filters( 'formipay/order/payment-details/'.$payment_gateway, $render, $form_id, $order_data, $submit_action_type );
ob_start();
echo wp_kses($render, formipay_thankyoupage_allowed_html());
$content = ob_get_contents();
ob_get_clean();
return $content;
}
public function add_payment_in_process_data($order_data, $form_id) {
$payment = explode(':::', $order_data['payment']);
$payment_gateway = $payment[0];
$order_data['payment_gateway'] = $payment_gateway;
return $order_data;
}
public function set_endpoint() {
add_rewrite_rule(
'^payment-confirm/([^/]*)/([^/]*)/?',
'index.php?formipay-payment-confirm=1&gateway=$matches[1]&formipay-token=$matches[2]',
'top'
);
flush_rewrite_rules();
}
public function set_query_vars($vars){
$vars[] = 'formipay-payment-confirm';
$vars[] = 'gateway';
$vars[] = 'formipay-token';
return $vars;
}
public function frontend_enqueue() {
global $wp_query;
if(
is_array($wp_query->query) &&
array_key_exists('formipay-payment-confirm', $wp_query->query) &&
true === boolval($wp_query->query['formipay-payment-confirm'])
) :
wp_enqueue_style( 'formipay-payment-confirm', FORMIPAY_URL . 'public/assets/css/payment-confirm.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'formipay-payment-confirm', FORMIPAY_URL . 'public/assets/js/payment-confirm.js', ['jquery'], FORMIPAY_VERSION, true );
endif;
}
public function payment_confirm_page_title($title) {
global $wp_query;
if(
is_array($wp_query->query) &&
array_key_exists('formipay-payment-confirm', $wp_query->query) &&
true === boolval($wp_query->query['formipay-payment-confirm'])
) :
$gateway = $wp_query->query['gateway'];
$gateway = strlen($gateway) > 3 ? ucwords( str_replace('_', ' ', $gateway) ) : strtoupper($gateway);
$title = $gateway . ' Confirmation' . ' - ' . get_bloginfo('name');
endif;
return $title;
}
}

932
includes/Product.php Normal file
View File

@@ -0,0 +1,932 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Product {
use SingletonTrait;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_action( 'init', [$this, 'cpt'] );
add_action( 'init', [$this, 'taxonomy'] );
add_action( 'admin_menu', [$this, 'add_submenu'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue_admin'] );
add_filter( 'stm_wpcfto_boxes', [$this, 'cpt_post_fields_box'] );
add_filter( 'stm_wpcfto_fields', [$this, 'cpt_post_fields_content'] );
add_filter( 'formipay/product-config', [$this, 'general_config'], 20 );
add_filter( 'formipay/product-config', [$this, 'variations_config'], 40 );
add_filter( 'formipay/product-config', [$this, 'stock_config'], 60 );
add_action( 'wp_ajax_formipay-tabledata-products', [$this, 'formipay_tabledata_products'] );
add_action( 'wp_ajax_formipay-create-product-post', [$this, 'formipay_create_product_post'] );
add_action( 'wp_ajax_formipay-delete-product', [$this, 'formipay_delete_product'] );
add_action( 'wp_ajax_formipay-bulk-delete-product', [$this, 'formipay_bulk_delete_product'] );
add_action( 'wp_ajax_formipay-duplicate-product', [$this, 'formipay_duplicate_product'] );
add_action( 'wp_ajax_formipay_product_get_currencies', [$this, 'formipay_product_get_currencies'] );
add_action( 'wp_ajax_get_product_variables', [$this, 'get_product_variables'] );
add_action('save_post', [$this, 'save_product'], 10, 2);
}
public function cpt() {
$labels = array(
'name' => __('Products', 'formipay'),
'singular_name' => __('Product', 'formipay'),
'menu_name' => 'Formipay',
'add_new' => __('Add New', 'formipay'),
'add_new_item' => __('Add New Product', 'formipay'),
'edit' => __('Edit', 'formipay'),
'edit_item' => __('Edit Product', 'formipay'),
'new_item' => __('New Product', 'formipay'),
'view' => __('View', 'formipay'),
'view_item' => __('View Product', 'formipay'),
'search_items' => __('Search Product', 'formipay'),
'not_found' => __('No products found', 'formipay'),
'not_found_in_trash' => __('No products found in trash', 'formipay'),
'parent' => __('Product Parent', 'formipay')
);
$args = array(
'label' => 'Products',
'labels' => $labels,
'public' => true,
'supports' => array( 'title' ),
'hierarchical' => true,
'taxonomies' => array( 'formipay-product-category' ),
'has_archive' => true,
'rewrite' => array( 'slug' => 'product' ),
'show_ui' => true,
'show_in_menu' => false,
'show_in_rest' => true,
'query_var' => true
);
register_post_type( 'formipay-product', $args );
}
public function taxonomy() {
register_taxonomy( 'formipay-product-category', 'formipay-product', array(
'rewrite' => array( 'slug' => 'product/category' ),
'hierarchical' => true
) );
}
public function add_submenu() {
add_submenu_page(
'formipay',
__( 'Products', 'formipay' ),
__( 'Products', 'formipay' ),
'manage_options',
'formipay-products',
[$this, 'formipay_products'],
);
add_submenu_page(
'formipay',
__('Categories', 'formipay'),
'└ ' . __('Categories', 'formipay'),
'manage_options',
'edit-tags.php?taxonomy=formipay-product-category&post_type=formipay-product',
null,
5
);
}
public function enqueue_admin() {
global $current_screen;
if($current_screen->id == 'formipay_page_formipay-products') {
wp_enqueue_style( 'page-products', FORMIPAY_URL . 'admin/assets/css/page-products.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'page-products', FORMIPAY_URL . 'admin/assets/js/page-products.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
wp_localize_script( 'page-products', 'formipay_products_page', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
'columns' => [
'id' => esc_html__( 'ID', 'formipay' ),
'title' => esc_html__( 'Title', 'formipay' ),
'price' => esc_html__( 'Price', 'formipay' ),
'type' => esc_html__( 'Type', 'formipay' ),
'stock' => esc_html__( 'Stock', 'formipay' ),
'status' => esc_html__( 'Status', 'formipay' ),
],
'filter_form' => [
'categories' => [
'placeholder' => esc_html__( 'Filter by Category', 'formipay' ),
'noresult_text' => esc_html__( 'No results found', 'formipay' )
],
'currencies' => [
'placeholder' => esc_html__( 'Filter by Currency', 'formipay' ),
'noresult_text' => esc_html__( 'No results found', 'formipay' )
]
],
'modal' => [
'form' => [
'currency_options' => formipay_currency_as_options()
],
'add' => [
'title' => esc_html__( 'Your New Product Data', 'formipay' ),
'validation' => esc_html__( '{{field}} is required', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Create New Product', 'formipay' )
],
'delete' => [
'question' => esc_html__( 'Do you want to delete the product?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Delete Permanently', 'formipay' )
],
'bulk_delete' => [
'question' => esc_html__( 'Do you want to delete the selected the product(s)?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
'duplicate' => [
'question' => esc_html__( 'Do you want to duplicate the product?', 'formipay' ),
'cancelButton' => esc_html__( 'Cancel', 'formipay' ),
'confirmButton' => esc_html__( 'Confirm', 'formipay' )
],
],
'nonce' => wp_create_nonce('formipay-admin-product-page')
] );
}
if($current_screen->base == 'post' && $current_screen->post_type == 'formipay-product'){
global $post;
$product_id = $post->ID;
// Get pricing method, default to 'auto' if not set
$pricing_method = formipay_get_post_meta($product_id, 'product_pricing_method') ?: 'auto';
$has_variation = (bool) formipay_get_post_meta($product_id, 'product_has_variation', true);
wp_enqueue_style( 'product-details', FORMIPAY_URL . 'admin/assets/css/admin-product-editor.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'product-details', FORMIPAY_URL . 'admin/assets/js/admin-product-editor.js', ['jquery', 'vue.js'], FORMIPAY_VERSION, true );
wp_localize_script( 'product-details', 'product_details', [
'default_currency' => formipay_default_currency(),
'default_currency_title' => formipay_default_currency('title'),
'default_currency_symbol' => formipay_default_currency('symbol'),
'default_currency_decimal_digits' => formipay_default_currency('decimal_digits'),
'default_currency_decimal_symbol' => formipay_default_currency('decimal_symbol'),
'default_currency_thousand_separator' => formipay_default_currency('thousand_separator'),
'product_type' => formipay_get_post_meta($product_id, 'product_type') ?: 'digital',
'product_is_physical' => formipay_get_post_meta($product_id, 'product_type') == 'physical',
'product_pricing_method' => $pricing_method, // Pass pricing method to JS
'product_has_variation' => $has_variation,
'variation_table' => [
'th_toggle' => __('Active', 'formipay'),
'th_name' => __('Title', 'formipay'),
'th_stock' => __('Stock', 'formipay'),
'th_price' => __('Regular Price', 'formipay'),
'th_sale' => __('Sale Price', 'formipay'),
'th_weight' => __('Weight', 'formipay'),
'th_prices_overall' => __('Prices', 'formipay'), // New header for manual mode
'manual_price_hint' => __('Prices set manually below', 'formipay'), // New hint for manual mode cell
'child_th_currency' => __('Currency', 'formipay'), // Child table header
'child_th_regular' => __('Regular Price', 'formipay'), // Child table header
'child_th_sale' => __('Sale Price', 'formipay'), // Child table header
'no_variations' => __( 'No variations available', 'formipay' )
]
] );
}
$screen = get_current_screen();
if($current_screen->taxonomy == 'formipay-product-category') {
wp_enqueue_style( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/css/admin-pages.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.mermaid.min.css', [], '6.2.0', 'all' );
wp_enqueue_style( 'page-product-taxonomy', FORMIPAY_URL . 'admin/assets/css/page-product-taxonomy.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'formipay-admin-pages', FORMIPAY_URL . 'admin/assets/js/admin-pages.js', ['jquery'], FORMIPAY_VERSION, true );
wp_enqueue_script( 'gridjs', FORMIPAY_URL . 'vendor/GridJS/gridjs.production.min.js', ['jquery'], '6.2.0', true );
wp_enqueue_script( 'page-product-taxonomy', FORMIPAY_URL . 'admin/assets/js/page-product-taxonomy.js', ['jquery', 'gridjs'], FORMIPAY_VERSION, true );
wp_localize_script( 'page-product-taxonomy', 'formipay_product_taxonomy_page', [
'site_url' => site_url(),
'page_title' => esc_html__( 'Product Categories', 'formipay' )
] );
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.4', 'all');
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.4', true);
wp_localize_script( 'sweetalert2', 'formipay_admin', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
] );
}
}
public function formipay_products() {
include_once FORMIPAY_PATH . 'admin/page-products.php';
}
public function cpt_post_fields_box($boxes) {
$boxes['formipay_product_settings'] = array(
'post_type' => array('formipay-product'),
'label' => __('Config', 'formipay'),
);
return $boxes;
}
public function cpt_post_fields_content($fields) {
$fields['formipay_product_settings'] = array();
$fields = apply_filters( 'formipay/product-config', $fields );
return $fields;
}
public function general_config($fields) {
// Product Details Group
$product_details_group = array(
'setting_product_details' => array(
'type' => 'group_title',
'label' => __( 'Product Details', 'formipay' ),
'description' => __( 'All details about the products sold using this form.', 'formipay' ),
'group' => 'started'
),
'product_type' => array(
'type' => 'radio',
'label' => __('Product Type', 'formipay'),
'options' => array(
'digital' => 'Digital',
'physical' => 'Physical'
),
'value' => 'digital',
'required' => true
),
'product_description' => array(
'type' => 'tinymce',
'label' => __('Description', 'formipay'),
)
);
$product_details_group = apply_filters( 'formipay/product-settings/tab:general/group:product-details', $product_details_group );
$last_product_details_group = array_key_last($product_details_group);
$product_details_group[$last_product_details_group]['group'] = 'ended';
// Product Currency Group
$product_currency_group = array(
'setting_product_pricing' => array(
'type' => 'group_title',
'label' => __( 'Pricing', 'formipay' ),
'description' => __( 'Carefully set these options below.', 'formipay' ),
'group' => 'started',
),
'product_pricing_method' => array(
'type' => 'radio',
'label' => __('Product Pricing Method', 'formipay'),
'options' => array(
'manual' => 'Manual Price Set',
'auto' => 'Auto via Currency Exchange API'
),
'value' => 'manual',
'required' => true
),
'product_regular_price' => array(
'type' => 'number',
'label' => sprintf( __('Regular Price (%s)', 'formipay'), formipay_default_currency('symbol') ),
'value' => 0,
'step' => 0.01,
'required' => true,
'dependency' => [
'key' => 'product_pricing_method',
'value' => 'auto'
]
),
'product_sale_price' => array(
'type' => 'number',
'label' => sprintf( __('Sale Price (%s)', 'formipay'), formipay_default_currency('symbol') ),
'value' => 0,
'step' => 0.01,
'dependency' => [
'key' => 'product_pricing_method',
'value' => 'auto'
]
),
'product_prices' => [
'type' => 'repeater',
'label' => __( 'Prices', 'formipay' ),
'description' => __( 'You can set price with multiple currency', 'formipay' ),
'fields' => [
'regular_price' => array(
'type' => 'number',
'label' => __('Regular Price', 'formipay'),
'value' => 0,
'step' => 0.01,
'required' => true
),
'sale_price' => array(
'type' => 'number',
'label' => __('Sale Price', 'formipay'),
'value' => 0,
'step' => 0.01
),
'currency' => array(
'type' => 'select',
'label' => __('Currency', 'formipay'),
'value' => 'IDR:::Indonesian rupiah:::Rp',
'options' => formipay_currency_as_options(),
'required' => true,
'searchable' => true,
'is_group_title' => true
),
'currency_decimal_digits' => array(
'type' => 'number',
'label' => __('Decimal Digits', 'formipay'),
'value' => '2',
'required' => true
),
'currency_decimal_symbol' => array(
'type' => 'text',
'label' => __('Decimal Symbol', 'formipay'),
'value' => '.',
'required' => true
),
'currency_thousand_separator' => array(
'type' => 'text',
'label' => __('Thousand Separator Symbol', 'formipay'),
'value' => ',',
'required' => true
),
],
'dependency' => [
'key' => 'product_pricing_method',
'value' => 'manual'
]
],
);
$product_currency_group = apply_filters( 'formipay/product-settings/tab:general/group:product-currency', $product_currency_group );
$last_product_currency_group = array_key_last($product_currency_group);
$product_currency_group[$last_product_currency_group]['group'] = 'ended';
$general_all_fields = array_merge($product_details_group, $product_currency_group);
$general_all_fields = apply_filters( 'formipay/product-settings/tab:general', $general_all_fields );
$fields['formipay_product_settings']['general'] = array(
'name' => __('General', 'formipay'),
'fields' => $general_all_fields
);
return $fields;
}
public function variations_config($fields) {
// Product Variations Attribute Group
$product_attributes_group = array(
'setting_product_variations' => array(
'type' => 'group_title',
'label' => __( 'Attributes', 'formipay' ),
'description' => __( 'First we need to build the attribute of this product. For example Color, Size, License Site Count.', 'formipay' ),
'group' => 'started'
),
'product_has_variation' => array(
'type' => 'checkbox',
'label' => __( 'Product has variations', 'formipay' ),
),
'product_variation_attributes' => array(
'type' => 'repeater',
'label' => __('Attributes', 'formipay'),
'description' => __( 'Your attributes will generate variation automatically.', 'formipay' ),
'fields' => [
'attribute_name' => [
'type' => 'text',
'label' => __( 'Attribute Name', 'formipay' ),
'description' => __( 'e.g. Color, Size, etc', 'formipay' ),
'is_group_title' => true
],
'attribute_variations' => [
'type' => 'repeater',
'label' => esc_html__( 'Variation', 'formipay' ),
'fields' => [
'variation_label' => [
'type' => 'text',
'label' => __( 'Title', 'formipay' ),
'description' => __( 'e.g. Color, Size, etc', 'formipay' ),
'required' => true,
'is_group_title' => true
],
'variation_value_type' => [
'type' => 'select',
'label' => __( 'Value Type', 'formipay' ),
'options' => [
'text' => __( 'Text', 'formipay' ),
'number' => __( 'Number', 'formipay' ),
'color' => __( 'Color', 'formipay' ),
],
'value' => 'text'
],
'variation_value_text' => [
'type' => 'text',
'label' => __( 'Value', 'formipay' ),
'required' => true,
'dependency' => array(
'key' => 'variation_value_type',
'value' => 'text'
),
],
'variation_value_number' => [
'type' => 'number',
'label' => __( 'Value', 'formipay' ),
'required' => true,
'dependency' => array(
'key' => 'variation_value_type',
'value' => 'number'
),
],
'variation_value_color' => [
'type' => 'color',
'label' => __( 'Value', 'formipay' ),
'required' => true,
'dependency' => array(
'key' => 'variation_value_type',
'value' => 'color'
),
],
],
],
],
'dependency' => array(
'key' => 'product_has_variation',
'value' => 'not_empty'
),
),
);
$product_attributes_group = apply_filters( 'formipay/product-settings/tab:general/group:product-attributes', $product_attributes_group );
$last_product_attributes_group = array_key_last($product_attributes_group);
$product_attributes_group[$last_product_attributes_group]['group'] = 'ended';
// Product Variations Attribute Group
// Define your product variations field group somewhere in your plugin/theme
$product_variations_table_html = file_get_contents(FORMIPAY_PATH . 'admin/templates/product-variations.php');
$product_variations_group = [
'variation_table' => [
'type' => 'html',
'label' => __( 'Variations', 'formipay' ),
'html' => $product_variations_table_html
],
];
$variation_all_fields = array_merge($product_attributes_group, $product_variations_group);
$variation_all_fields = apply_filters( 'formipay/product-settings/tab:variations', $variation_all_fields );
$fields['formipay_product_settings']['variation'] = array(
'name' => __('Variations', 'formipay'),
'fields' => $variation_all_fields
);
return $fields;
}
public function stock_config($fields) {
// Product Quantity Group
$product_quantity_group = array(
'setting_product_quantity' => array(
'type' => 'group_title',
'label' => __( 'Quantity Settings', 'formipay' ),
'description' => __( 'You can activate this if you will manage the stock.', 'formipay' ),
'group' => 'started'
),
'product_quantity_toggle' => array(
'type' => 'checkbox',
'label' => __( 'Activate Quantity', 'formipay' ),
),
'product_quantity_range' => array(
'type' => 'number',
'label' => __( 'Quantity Range Step', 'formipay' ),
'value' => 1,
'dependency' => array(
'key' => 'product_quantity_toggle',
'value' => 'not_empty'
)
),
'product_minimum_purchase' => array(
'type' => 'number',
'label' => __( 'Minimum Purchase', 'formipay' ),
'value' => 1,
'dependency' => array(
'key' => 'product_quantity_toggle',
'value' => 'not_empty'
)
),
'product_stock' => array(
'type' => 'number',
'label' => __( 'Stock', 'formipay' ),
'description' => __('Set -1 for unlimited stock', 'formipay'),
'value' => -1,
'dependency' => array(
'key' => 'product_quantity_toggle',
'value' => 'not_empty'
)
)
);
$product_quantity_group = apply_filters( 'formipay/product-settings/tab:stock/group:product-quantity', $product_quantity_group );
$last_product_quantity_group = array_key_last($product_quantity_group);
$product_quantity_group[$last_product_quantity_group]['group'] = 'ended';
$stock_all_fields = apply_filters( 'formipay/product-settings/tab:stock', $product_quantity_group );
$fields['formipay_product_settings']['stock'] = array(
'name' => __('Stock & Quantity', 'formipay'),
'fields' => $stock_all_fields
);
return $fields;
}
public function formipay_tabledata_products() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
global $wpdb;
// Initialize default args
$base_args = [
'post_type' => 'formipay-product',
'post_status' => ['publish', 'draft', 'pending'],
'fields' => 'ids' // Optimize initial query
];
// Handle status filtering
if (isset($_REQUEST['post_status']) && 'all' !== $_REQUEST['post_status']) {
$base_args['post_status'] = [sanitize_key($_REQUEST['post_status'])];
}
// Currency filtering
if (!empty($_REQUEST['currency'])) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
$base_args['meta_query'] = [
[
'key' => 'product_currency',
'value' => sanitize_text_field(wp_unslash($_REQUEST['currency'])),
'compare' => 'LIKE'
]
];
}
// Category filtering
if (!empty($_REQUEST['category'])) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_tax_query
$base_args['tax_query'] = [
[
'taxonomy' => 'formipay-product-category',
'field' => 'term_id',
'terms' => absint($_REQUEST['category'])
]
];
}
// Search functionality
if (!empty($_REQUEST['search'])) {
$base_args['s'] = isset($_REQUEST['search']) ? sanitize_text_field(wp_unslash($_REQUEST['search'])) : '';
}
// Sorting
if (!empty($_REQUEST['orderby'])) {
$orderby = sanitize_key($_REQUEST['orderby']);
$order = isset($_REQUEST['sort']) ? sanitize_key($_REQUEST['sort']) : 'ASC';
if ('price' === $orderby) {
// phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
$base_args['meta_key'] = 'product_price';
$base_args['orderby'] = 'meta_value_num';
} else {
$base_args['orderby'] = $orderby;
}
$base_args['order'] = $order;
}
// Pagination
if (isset($_REQUEST['limit'])) {
$base_args['posts_per_page'] = absint($_REQUEST['limit']);
}
if (isset($_REQUEST['offset'])) {
$base_args['offset'] = intval($_REQUEST['offset']);
}
// Main query
$query = new \WP_Query($base_args);
$product_ids = $query->posts;
$total_products = $query->found_posts;
// Process results
$products = [];
foreach ($product_ids as $product_id) {
$product = get_post($product_id);
$currency = explode(':::', get_post_meta($product_id, 'product_currency', true));
$categories = wp_get_post_terms($product_id, 'formipay-product-category', ['fields' => 'names']);
$products[] = [
'ID' => $product_id,
'title' => $product->post_title,
'category' => implode(', ', $categories),
'type' => formipay_get_post_meta($product_id, 'product_type'),
'stock' => formipay_get_post_meta($product_id, 'stock') ?: '∞',
'price' => [
'flag' => formipay_get_flag_by_currency($currency[0] ?? ''),
'name' => formipay_price_format(
get_post_meta($product_id, 'price', true),
$product_id
)
],
'status' => $product->post_status,
];
}
// Get status counts using WordPress API
$counts = wp_count_posts('formipay-product');
$status_counts = [];
foreach ($counts as $status => $count) {
$status_counts[$status] = $count;
}
$status_counts['all'] = array_sum($status_counts);
wp_send_json([
'results' => $products,
'total' => $total_products,
'posts_report' => array_map('intval', (array)$status_counts)
]);
}
public function formipay_create_product_post() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
$title = isset($_REQUEST['title']) ? sanitize_text_field( wp_unslash($_REQUEST['title']) ) : '';
if( !empty($title) && '' !== $title ){
$post_id = wp_insert_post( [
'post_title' => $title,
'post_type' => 'formipay-product',
], true );
if(is_wp_error($post_id)){
wp_send_json_error( [
'message' => $post_id->get_error_message()
] );
}
update_post_meta($post_id, 'product_currency', 'USD:::United States dollar:::$'); // Note:: Ganti dengan default currency dari global settings
wp_send_json_success( [
'edit_post_url' => admin_url('post.php?post='.$post_id.'&action=edit')
] );
}
wp_send_json_error( [
'message' => esc_html__( 'Product\'s title is empty.', 'formipay' )
] );
}
public function formipay_product_get_currencies() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
$search = isset($_REQUEST['search']) ? sanitize_text_field( wp_unslash($_REQUEST['search']) ) : '';
$countries = formipay_currency_array();
$results = [];
foreach($countries as $country){
$country_name = strtolower($country['name']);
$country_search = strtolower($search);
if(strpos($country_name, $country_search) !== false || $country_search == strtolower($country['code'])){
$results[] = [
'value' => $country['code'],
// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage -- This image is a plugin asset and not a WordPress attachment.
'label' => '<img src="'.formipay_get_flag_by_currency($country['code']).'" height="18" style="margin-bottom: -3px;"> '.$country['name']
];
}
}
wp_send_json( $results );
}
public function formipay_delete_product() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
$form_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$delete = wp_delete_post($form_id, true);
if(is_wp_error( $delete )){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'Failed to delete product. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
wp_send_json_success( [
'title' => esc_html__( 'Deleted', 'formipay' ),
'message' => esc_html__( 'Product is deleted permanently.', 'formipay' ),
'icon' => 'success'
] );
}
public function formipay_bulk_delete_product() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
if( empty($_REQUEST['ids']) ){
wp_send_json_error( [
'title' => esc_html__( 'Failed', 'formipay' ),
'message' => esc_html__( 'There is no product selected. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$ids = isset($_REQUEST['ids']) ? $_REQUEST['ids'] : [];
$success = 0;
$failed = 0;
if(!empty($ids)){
foreach($ids as $id){
$delete = wp_delete_post($id, true);
if(is_wp_error( $delete )){
$failed++;
}else{
$success++;
}
}
}
if($success > 0){
$report .= sprintf( __( ' Deleted %d product(s).', 'formipay' ), $success);
}
if($failed > 0){
$report .= sprintf( __( ' Failed %d product(s).', 'formipay' ), $failed);
}
wp_send_json_success( [
'title' => esc_html__( 'Done!', 'formipay' ),
'message' => $report,
'icon' => 'info'
] );
}
public function formipay_duplicate_product() {
check_ajax_referer( 'formipay-admin-product-page', '_wpnonce' );
$post_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : '';
$post = get_post($post_id);
if (!$post) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Product is not defined.', 'formipay' ),
'icon' => 'error'
] );
}
if (!in_array($post->post_type, ['formipay-product'])) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Wrong Post Type.', 'formipay' ),
'icon' => 'error'
] );
}
$duplicate_post = [
'post_title' => $post->post_title . ' (Copy)',
'post_content' => $post->post_content,
'post_status' => 'draft', // Set sebagai draft
'post_type' => $post->post_type,
'post_author' => $post->post_author,
];
$new_post_id = wp_insert_post($duplicate_post);
if (is_wp_error($new_post_id)) {
wp_send_json_error( [
'title' => esc_html__('Failed', 'formipay'),
'message' => esc_html__( 'Something happened. Please try again.', 'formipay' ),
'icon' => 'error'
] );
}
$meta_data = get_post_meta($post_id);
foreach ($meta_data as $key => $values) {
foreach ($values as $value) {
update_post_meta($new_post_id, $key, maybe_unserialize($value));
}
}
$taxonomies = get_object_taxonomies($post->post_type);
foreach ($taxonomies as $taxonomy) {
$terms = wp_get_object_terms($post_id, $taxonomy, ['fields' => 'slugs']);
wp_set_object_terms($new_post_id, $terms, $taxonomy);
}
wp_send_json_success( [
'title' => esc_html__('Duplicated!', 'formipay'),
'message' => esc_html__( 'Product is successfully duplicated.', 'formipay' ),
'icon' => 'success'
] );
}
public function get_product_variables() {
$post_id = intval($_POST['post_id'] ?? 0);
$data = get_post_meta($post_id, 'product_variables', true);
$json = is_string($data) ? json_decode($data, true) : [];
if(is_array($json)){
wp_send_json_success( $json );
}
wp_send_json_error();
}
public function save_product_depracated($post_id, $post) {
// Verify nonce and permissions here if you have a nonce field (recommended)
// Avoid autosave and revision saves
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (wp_is_post_revision($post_id)) return;
// Check user permissions
if (!current_user_can('edit_post', $post_id)) return;
if (isset($_POST['product_variables'])) {
// Sanitize input (JSON string)
$product_variables_json = wp_unslash($_POST['product_variables']); // Remove slashes added by WP
$product_variables = json_decode($product_variables_json, true);
if (json_last_error() === JSON_ERROR_NONE && is_array($product_variables)) {
// Save the meta as JSON string or serialized array
update_post_meta($post_id, 'product_variables', $product_variables_json);
} else {
// Invalid JSON, optionally delete meta or handle error
delete_post_meta($post_id, 'product_variables');
}
}
}
function save_product( $post_id ) {
// 1. Verifikasi Nonce untuk keamanan
if ( ! isset( $_POST['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'update-post_' . $post_id ) ) {
return;
}
// 2. Abaikan jika ini adalah autosave
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
// 3. Periksa izin pengguna
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
// 4. Periksa apakah data 'product_variables' dikirim
if ( ! isset( $_POST['product_variables'] ) ) {
// Jika tidak ada data, mungkin kita ingin menghapus meta yang lama
delete_post_meta( $post_id, 'product_variables' );
return;
}
// 5. Ambil data, lakukan sanitasi dasar, dan simpan
$variations_data_json = $_POST['product_variables'];
// Sanitasi: wp_slash() penting untuk memastikan JSON string disimpan dengan benar,
// terutama jika mengandung karakter kutip.
$sanitized_variations_data = wp_unslash( $variations_data_json );
// Simpan data ke database. WordPress akan menangani serialisasi jika perlu.
update_post_meta( $post_id, 'product_variables', $sanitized_variations_data );
}
}

638
includes/Render.php Normal file
View File

@@ -0,0 +1,638 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
use Formipay\Field;
// Exit if accessed directly
if ( ! defined( 'ABSPATH' ) ) exit;
class Render {
use SingletonTrait;
private $display_label;
private $show_label;
private $asterisk;
private static $form_ids = [];
/**
* Register hooks.
*/
private function __construct() {
add_shortcode('formipay', [$this, 'shortcode']);
add_action('wp_enqueue_scripts', [$this, 'public_enqueue_style'] );
}
/**
* Shortcode handler.
*
* @param array $atts
* @param string|null $content
* @return string|false
*/
public function shortcode($atts, $content = null) {
$atts = shortcode_atts(['form' => 0], $atts, 'formipay');
if ($atts['form'] == 0) {
return false;
}
$post_id = intval($atts['form']);
$currency = 'Rp. ';
if ($post_id > 0) {
$currency = formipay_post_currency($post_id);
self::$form_ids[] = $post_id;
}
$form_settings = get_post_meta($post_id, 'formipay_settings', true);
$point_symbol = formipay_get_post_meta($post_id, 'multistep_point_symbol');
$point_icons = formipay_get_post_meta($post_id, 'multistep_point_icons');
$page_breaks = [];
if(!empty($form_settings['fields'])){
foreach($form_settings['fields'] as $field){
if($field['field_type'] == 'page_break'){
if($point_symbol == 'icon'){
$icons = json_decode($point_icons, true);
foreach($icons as $icon){
if($icon['field_id'] == $field['field_id']){
$field['icon'] = $icon['script'];
}
}
}
$page_breaks[] = $field;
}
}
}
ob_start();
$timezone = get_option('timezone_string', 'Asia/Jakarta');
$require_login = formipay_get_post_meta($post_id, 'require_login') === 'on';
$scheduled = formipay_get_post_meta($post_id, 'schedule_toggle') === 'on';
$render = true;
if ($require_login && !is_user_logged_in()) {
$render = false;
echo '<div class="formipay-message">' . wp_kses_post(formipay_get_post_meta($post_id, 'require_login_message')) . '</div>';
} elseif ($scheduled) {
$date_range = explode(',', formipay_get_post_meta($post_id, 'daterange'));
$date_from = new DateTime($date_range[0], new DateTimeZone($timezone));
$date_to = new DateTime($date_range[1], new DateTimeZone($timezone));
$now = new DateTime('now', new DateTimeZone($timezone));
if ($date_from > $now) {
$render = false;
?>
<div class="formipay-message">
<?php echo wp_kses_post(formipay_get_post_meta($post_id, 'waiting_message')); ?>
</div>
<?php
} elseif ($now > $date_to) {
$render = false;
?>
<div class="formipay-message">
<?php echo wp_kses_post(formipay_get_post_meta($post_id, 'expired_message')); ?>
</div>
<?php
}
}
if ($render && $form_settings && is_array($form_settings['fields']) && count($form_settings['fields']) > 0) {
$this->public_enqueue_scripts();
$button_background_color = json_decode(formipay_get_post_meta($post_id, 'button_bg_color'), true );
$button_text_color = json_decode(formipay_get_post_meta($post_id, 'button_text_color'), true );
$button_border_color = json_decode( formipay_get_post_meta($post_id, 'button_border_color'), true );
?>
<div class="formipay-form-wrapper">
<style>
body {
--formipay-button-submit-bg-color: <?php echo esc_html($button_background_color['regular'] ?? '#000000'); ?>;
--formipay-button-submit-bg-color-hover: <?php echo esc_html($button_background_color['hover'] ?? '#ffffff'); ?>;
--formipay-button-submit-bg-color-active: <?php echo esc_html($button_background_color['active'] ?? '#ffffff'); ?>;
--formipay-button-submit-text-color: <?php echo esc_html($button_text_color['regular'] ?? '#ffffff'); ?>;
--formipay-button-submit-text-color-hover: <?php echo esc_html($button_text_color['hover'] ?? '#000000') ?>;
--formipay-button-submit-text-color-active: <?php echo esc_html($button_text_color['active'] ?? '#000000'); ?>;
--formipay-button-submit-border-color: <?php echo esc_html($button_border_color['regular'] ?? '#000000'); ?>;
--formipay-button-submit-border-color-hover: <?php echo esc_html($button_border_color['hover'] ?? '#ffffff'); ?>;
--formipay-button-submit-border-color-active: <?php echo esc_html($button_border_color['active'] ?? '#ffffff'); ?>;
--formipay-required-field-sign-color: <?php echo esc_html(formipay_get_post_meta($post_id, 'required_field_color')); ?>;
--formipay-payment-desktop-columns: <?php echo intval(formipay_get_post_meta($post_id, 'payment_desktop_columns') ?? 4); ?>;
--formipay-payment-tablet-columns: <?php echo intval(formipay_get_post_meta($post_id, 'payment_tablet_columns') ?? 3) ?>;
--formipay-payment-mobile-columns: <?php echo intval(formipay_get_post_meta($post_id, 'payment_mobile_columns') ?? 2) ?>;
<?php
if(formipay_isPopup($post_id)){
$popup_button_background_color = json_decode(formipay_get_post_meta($post_id, 'popup_button_bg_color'), true );
$popup_button_text_color = json_decode(formipay_get_post_meta($post_id, 'popup_button_text_color'), true );
$popup_button_border_color = json_decode( formipay_get_post_meta($post_id, 'popup_button_border_color'), true );
?>
--formipay-popup-button-bg-color: <?php echo esc_html($popup_button_background_color['regular'] ?? '#000000'); ?>;
--formipay-popup-button-bg-color-hover: <?php echo esc_html($popup_button_background_color['hover'] ?? '#ffffff'); ?>;
--formipay-popup-button-bg-color-active: <?php echo esc_html($popup_button_background_color['active'] ?? '#ffffff'); ?>;
--formipay-popup-button-text-color: <?php echo esc_html($popup_button_text_color['regular'] ?? '#ffffff'); ?>;
--formipay-popup-button-text-color-hover: <?php echo esc_html($popup_button_text_color['hover'] ?? '#000000'); ?>;
--formipay-popup-button-text-color-active: <?php echo esc_html($popup_button_text_color['active'] ?? '#000000'); ?>;
--formipay-popup-button-border-color: <?php echo esc_html($popup_button_border_color['regular'] ?? '#000000'); ?>;
--formipay-popup-button-border-color-hover: <?php echo esc_html($popup_button_border_color['hover'] ?? '#ffffff'); ?>;
--formipay-popup-button-border-color-active: <?php echo esc_html($popup_button_border_color['active'] ?? '#ffffff'); ?>;
--formipay-popup-backdrop-color: <?php echo esc_html(formipay_get_post_meta($post_id, 'popup_content_wrapper_backdrop_color') ?? 'rgba(0,0,0,0.75)'); ?>;
--formipay-popup-wrapper-width: <?php echo esc_html(formipay_get_post_meta($post_id, 'popup_content_wrapper_width')); ?>px;
--formipay-popup-button-width: <?php echo esc_html(formipay_get_post_meta($post_id, 'popup_button_width') == 'fullwidth' ? '100%' : 'fit-content'); ?>;
<?php
}
if(count($page_breaks) > 0 && !is_admin()){
?>
--formipay-page-break-count: <?php echo count($page_breaks) + 1 ?>;
<?php
}
switch (formipay_get_post_meta($post_id, 'multistep_point_shape')) {
case 'rectangle':
?>
--formipay-step-shape-radius: 0%;
<?php
break;
case 'rounded':
?>
--formipay-step-shape-radius: 25%;
<?php
break;
default:
?>
--formipay-step-shape-radius: 50%;
<?php
break;
}
switch (formipay_get_post_meta($post_id, 'multistep_btn_size')) {
case 'smaller_than_submit':
?>
--formipay-step-button-size: .5em 1em;
--formipay-step-button-text-size: smaller;
--formipay-step-button-height: 32px;
<?php
break;
case 'larger_than_submit':
?>
--formipay-step-button-size: 1em 2em;
--formipay-step-button-text-size: larger;
--formipay-step-button-height: 48px;
<?php
break;
default:
?>
--formipay-step-button-size: .75em 1.5em;
--formipay-step-button-text-size: inherit;
--formipay-step-button-height: 40px;
<?php
break;
}
?>
}
</style>
<?php if(formipay_isPopup($post_id)) { ?>
<div class="formipay-popup modal" id="formipay-popup-<?php echo intval($post_id); ?>">
<?php } ?>
<form class="formipay-form" data-form-id="<?php echo intval($post_id); ?>" id="formipay-form-<?php echo intval($post_id); ?>">
<input type="hidden" id="product_name" value="<?php echo esc_attr(get_the_title($post_id)); ?>">
<input type="hidden" id="product_price" value="<?php echo esc_attr(formipay_get_post_meta($post_id, 'product_price')); ?>">
<?php if(formipay_isPopup($post_id) && !empty(formipay_get_post_meta($post_id, 'popup_content_wrapper_title'))) { ?>
<h4 class="formipay-popup-title"><?php echo esc_html(formipay_get_post_meta($post_id, 'popup_content_wrapper_title')); ?></h4>
<?php } ?>
<div class="formipay-form-body formipay-<?php echo (count($page_breaks) > 0 && !is_admin()) ? '1' : esc_attr(formipay_get_post_meta($post_id, 'form_layout_columns')); ?>-cols">
<div class="form-fields-wrapper">
<?php if(!empty(formipay_get_post_meta($post_id, 'product_description'))) { ?>
<div class="formipay-description">
<?php echo wp_kses_post(formipay_get_post_meta($post_id, 'product_description')); ?>
</div>
<?php } ?>
<?php
$label_show = formipay_get_post_meta($post_id, 'label_visibility') !== 'hide';
$this->display_label = !$label_show ? ' formipay-hidden-element' : '';
$this->asterisk = formipay_get_post_meta($post_id, 'required_field_sign') === 'text' ? formipay_get_post_meta($post_id, 'required_text') : '(*)';
if(count($page_breaks) > 0 && !is_admin()){
$this->render_top_pagination_process($page_breaks, $point_symbol);
}
foreach ($form_settings['fields'] as $field_id => $field) {
$field_id = str_replace('_config', '', $field_id);
$field['field_id'] = $field_id;
$field['placeholder'] = formipay_get_post_meta($post_id, 'field_placeholder') === 'label'
? ($field['label'] ?? '')
: ($field['placeholder'] ?? '');
$field['calculable'] = in_array($field['field_type'], ['hidden', 'select', 'checkbox', 'radio']) ? ' formipay-input-calculable' : '';
$field['calc_value'] = $field['calc_value'] ?? 0;
$field['calc'] = in_array($field['field_type'], ['hidden']) ? 'data-calc-value="' . $field['calc_value'] . '"' : '';
$this->render_field($post_id, $field_id, $field);
}
?>
<?php
if(count($page_breaks) > 0 && !is_admin()){
?>
<div class="formipay-field-group formipay-page-break formipay-page-break-payment">
<div class="formipay-page-break-information">
<label for="<?php echo esc_attr($field_id); ?>" class="formipay-label divider-label <?php echo esc_attr($display_label); ?>">
<?php echo esc_html__('Payment', 'formipay'); ?>
</label>
<span class="divider-line"></span>
</div>
</div>
<?php
}
$this->render_bottom_pagination_process($post_id, $page_breaks);
?>
</div>
<div class="formipay-static-elements">
<div class="result-wrapper">
<input class="formipay-meta-input" type="hidden" name="user_id" value="<?php echo is_user_logged_in() ? esc_attr( absint( get_current_user_id() ) ) : ''; ?>">
<input class="formipay-meta-input" type="hidden" name="session_id" value="<?php echo esc_attr( sanitize_text_field( uniqid('', true) ) ); ?>">
<input class="formipay-meta-input" type="hidden" name="referrer" value="<?php echo isset($_SERVER['HTTP_REFERER']) ? esc_attr( esc_url_raw( wp_unslash( $_SERVER['HTTP_REFERER'] ) ) ) : ''; ?>">
<input class="formipay-meta-input" type="hidden" name="page_url" value="<?php echo isset($_SERVER['REQUEST_URI']) ? esc_attr( esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) : ''; ?>">
<input class="formipay-meta-input" type="hidden" name="timestamp" value="<?php echo esc_attr( absint( time() ) ); ?>">
<input class="formipay-meta-input" type="hidden" name="utm_source" value="<?php echo
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
isset($_GET['utm_source']) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['utm_source'] ) ) ) : '';
?>">
<input class="formipay-meta-input" type="hidden" name="utm_medium" value="<?php echo
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
isset($_GET['utm_medium']) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['utm_medium'] ) ) ) : '';
?>">
<input class="formipay-meta-input" type="hidden" name="utm_campaign" value="<?php echo
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
isset($_GET['utm_campaign']) ? esc_attr( sanitize_text_field( wp_unslash( $_GET['utm_campaign'] ) ) ) : '';
?>">
<input class="formipay-meta-input" type="hidden" name="ip_address" value="<?php echo
isset($_SERVER['REMOTE_ADDR']) ? esc_attr( sanitize_text_field( wp_unslash($_SERVER['REMOTE_ADDR']) ) ) : '';
?>">
<input class="formipay-meta-input" type="hidden" name="user_agent" value="<?php echo
isset($_SERVER['HTTP_USER_AGENT']) ? esc_attr( sanitize_text_field( wp_unslash($_SERVER['HTTP_USER_AGENT']) ) ) : '';
?>">
<input class="formipay-meta-input" type="hidden" name="device_type" value="<?php echo esc_attr( ( strpos( sanitize_text_field( wp_unslash($_SERVER['HTTP_USER_AGENT']) ), 'Mobile' ) !== false ) ? 'Mobile' : 'Desktop' ); ?>">
<?php
$static_element_sort = json_decode(formipay_get_post_meta($post_id, 'form_static_elements_sort'), true);
$static_element_active = $static_element_sort[1]['options'];
if(!empty($static_element_active)){
foreach($static_element_active as $element){
switch ($element['id']) {
case 'payment_methods':
$this->render_payment_options($post_id);
break;
case 'coupon_fields':
?>
<div class="formipay-field-group">
<div class="formipay-coupon-field-group">
<input type="text" name="coupon_code"
class="formipay-input formipay-code-input"
value="<?php echo
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
isset($_GET['coupon']) ? esc_attr( sanitize_text_field( wp_unslash($_GET['coupon'] ) ) ) : ''
?>" placeholder="Your coupon code"
data-label="Coupon Code">
<button type="button" data-text="<?php echo esc_html__( 'Apply', 'formipay' ); ?>" data-checking="<?php echo esc_html__( 'Checking...', 'formipay' ); ?>" id="apply_coupon_code"><?php echo esc_html__( 'Apply', 'formipay' ); ?></button>
</div>
<div class="formipay-coupon-alert-message"></div>
</div>
<?php
break;
case 'order_review':
?>
<div class="form-calculation form-calculate-<?php echo esc_attr($post_id); ?>">
<h4><?php echo esc_html(formipay_get_post_meta($post_id, 'order_review_title')); ?></h4>
<table id="formipay-review-order">
<tbody>
<tr class="formipay-product-row formipay-item-row main">
<?php
$price = formipay_get_post_meta($post_id, 'product_price');
if(formipay_get_post_meta($post_id, 'product_quantity_toggle') == 'on') {
$stock = formipay_get_post_meta($post_id, 'product_stock');
$stock_html = '';
if($stock > -1){
$stock = ' max="'.$stock.'"';
}
?>
<th>
<?php echo esc_html(get_the_title($post_id)); ?> <br>
<span class="product-qty-wrapper">
<button type="button" class="product-qty qty-min">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14" />
</svg>
</button>
<input type="number" class="product-qty formipay-qty-input" value="<?php echo intval(formipay_get_post_meta($post_id, 'product_quantity_range')); ?>" step="<?php echo intval(formipay_get_post_meta($post_id, 'product_quantity_range')); ?>" min="<?php echo intval(formipay_get_post_meta($post_id, 'product_minimum_purchase')); ?>"<?php echo esc_html($stock) ?>>
<button type="button" class="product-qty qty-plus">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 5v14m-7-7h14" />
</svg>
</button>
</span>
</th>
<td class="product_price"><?php echo esc_html(formipay_price_format(floatval($price) * intval(formipay_get_post_meta($post_id, 'product_quantity_range')), $post_id)); ?></td>
<?php
} else {
?>
<th>
<?php echo esc_html(get_the_title($post_id)); ?> <input type="hidden" class="formipay-qty-input" value="1">
</th>
<td><?php echo esc_html(formipay_price_format(floatval($price), $post_id)); ?></td>
<?php
}
?>
</tr>
<tr class="formipay-total-row">
<td colspan="2"></td>
</tr>
<tr class="formipay-grand-total-row">
<th><?php echo esc_html__( 'Total', 'formipay' ); ?></th>
<td class="grand_total"><?php echo esc_html(formipay_price_format(floatval($price), $post_id)); ?></td>
</tr>
</tbody>
</table>
</div>
<?php
break;
case 'submit_button':
?>
<button type="submit" class="formipay-submit-button"
data-button-text="<?php echo esc_attr(formipay_get_post_meta($post_id, 'button_text')); ?>"
style="width: <?php echo formipay_get_post_meta($post_id, 'button_width') == 'fit-content' ? 'fit-content' : '100%' ?>;
margin-left: <?php echo formipay_get_post_meta($post_id, 'button_position') !== 'left' ? 'auto' : 'unset' ?>;
margin-right: <?php echo formipay_get_post_meta($post_id, 'button_position') !== 'right' ? 'auto' : 'unset' ?>;">
<?php echo esc_html(formipay_get_post_meta($post_id, 'button_text')); ?> - <?php echo esc_html(formipay_price_format(floatval($price), $post_id)); ?>
</button>
<?php
break;
case 'submit_response_notice':
?>
<div class="submit-response formipay-message" style="display: none;"></div>
<?php
break;
default:
do_action('formipay/render/static-element', $element['id']);
break;
}
}
}
?>
</div>
</div>
</div>
</form>
<?php if(formipay_isPopup($post_id) && false == boolval(formipay_get_post_meta($post_id, 'popup_click_selector'))) { ?>
</div>
<div class="formipay-popup-trigger">
<a data-modal="#formipay-popup-<?php echo intval($post_id); ?>"
class="formipay-open-popup-button"
style="width: <?php echo formipay_get_post_meta($post_id, 'popup_button_width') == 'fit-content' ? 'fit-content' : '100%' ?>;
margin-left: <?php echo formipay_get_post_meta($post_id, 'popup_button_alignment') !== 'left' ? 'auto' : 'unset' ?>;
margin-right: <?php echo formipay_get_post_meta($post_id, 'popup_button_alignment') !== 'right' ? 'auto' : 'unset' ?>;">
<?php echo esc_html(formipay_get_post_meta($post_id, 'popup_button_text')); ?>
</a>
</div>
<?php } ?>
</div>
<?php
}
return ob_get_clean();
}
private function render_field($post_id, $field_id, $field) {
$type = $field['field_type'];
// Normalize method name for Field class
$method = "render_{$type}_field";
if (method_exists(Field::get_instance(), $method)) {
echo Field::get_instance()->$method($field);
} else {
// Optionally handle unknown field types
do_action('formipay/render/custom-field-type', $type, $field);
}
}
/**
* Render payment options
*/
private function render_payment_options($post_id) {
$payments = apply_filters( 'formipay/frontend/payment-list', [], $post_id );
?>
<div class="formipay-field-group">
<label class="formipay-label"><?php echo esc_html(formipay_get_post_meta($post_id, 'payment_section_title')); ?></label>
<div class="formipay-payments">
<?php
$unique_code = wp_rand(1, 999);
if(!empty($payments)){
foreach ($payments as $value => $payment) {
$channel = explode(':::', $value);
$gateway = $channel[0];
$payment_non_label_class = formipay_get_post_meta($post_id, 'payment_label') == 'on' ? '' : 'payment-logo-only';
$logo = '';
// Check if payment logo is an attachment ID or URL
if (!empty($payment[$gateway.'_logo'])) {
if (is_numeric($payment[$gateway.'_logo'])) {
// Media library image
$logo = wp_get_attachment_image(
intval($payment[$gateway.'_logo']),
['100', '40'],
false,
[
'class' => 'formipay-payment-logo formipay-'.$gateway.'-logo '.$payment_non_label_class,
'loading' => 'lazy'
]
);
} else {
// Static plugin image (add PHPCS ignore)
$logo_url = $payment[$gateway.'_logo'];
// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
$logo = '<img src="' . esc_url($logo_url) . '"
width="100"
height="40"
class="formipay-payment-logo formipay-'.$gateway.'-logo '.$payment_non_label_class.'"
alt="' . esc_attr($payment[$gateway.'_name']) . '"
loading="lazy">';
}
}
?>
<div class="formipay-payment-option-group">
<label for="<?php echo esc_attr($value) ?>" class="formipay-label">
<input type="radio"
name="payment"
class="formipay-input formipay-input-calculable formipay-payment-channel"
id="<?php echo esc_attr($value); ?>"
value="<?php echo esc_attr($value); ?>"
required>
<?php echo $logo; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if(formipay_get_post_meta($post_id, 'payment_label') == 'on') { ?>
<span class="formipay-payment-name formipay-<?php echo esc_attr($gateway) ?>-name">
<?php echo esc_html($payment[$gateway.'_name']); ?>
</span>
<?php } ?>
</label>
</div>
<?php
}
}
?>
</div>
</div>
<?php
}
/**
* Render progess pages (on top)
*/
private function render_top_pagination_process($page_breaks, $point_symbol){
$payment_symbol = count($page_breaks) + 1;
if($point_symbol == 'icon'){
$payment_symbol = '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19H6a3 3 0 0 1-3-3V8a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v4.5M3 10h18m-5 9h6m-3-3l3 3l-3 3M7.005 15h.005M11 15h2" />
</svg>';
}
if(!empty($page_breaks)){
?>
<div class="formipay-top-pagination">
<?php
foreach($page_breaks as $index => $page){
$index = $index + 1;
$symbol = $index;
if($point_symbol == 'icon'){
$symbol = '<span class="pagination-icon">'.$page['icon'].'</span>';
}
?>
<div class="formipay-progress" data-page-number="<?php echo intval($index) ?>">
<div class="formipay-index-wrapper">
<span class="formipay-page-index"><?php echo wp_kses_post($symbol) ?></span>
<span class="formipay-page-label"><?php echo esc_html($page['label']) ?></span>
</div>
</div>
<?php
}
?>
<div class="formipay-progress" data-page-number="<?php echo count($page_breaks) + 1 ?>">
<div class="formipay-index-wrapper">
<span class="formipay-page-index"><?php echo wp_kses_post($payment_symbol); ?></span>
<span class="formipay-page-label"><?php echo esc_html__( 'Payment', 'formipay' ); ?></span>
</div>
</div>
</div>
<?php
}
}
private function render_bottom_pagination_process($post_id, $page_breaks) {
if(!empty($page_breaks)){
?>
<div class="formipay-bottom-pagination">
<button type="button" class="formipay-page-break-prev-button" disabled>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h6m3 0h1.5m3 0h.5M5 12l4 4m-4-4l4-4" />
</svg>
<?php echo esc_html(formipay_get_post_meta($post_id, 'multistep_btn_prev_text')); ?>
</button>
<button type="button" class="formipay-page-break-next-button">
<?php echo esc_html(formipay_get_post_meta($post_id, 'multistep_btn_next_text')); ?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h.5m3 0H10m3 0h6m-4 4l4-4m-4-4l4 4" />
</svg>
</button>
</div>
<?php
}
}
/**
* Enqueue public scripts and styles.
*/
public function public_enqueue_style() {
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.5', 'all');
wp_enqueue_style( 'jquery-modal', FORMIPAY_URL . 'vendor/jQueryModal/jquery.modal.min.css', [], '0.9.1', 'all');
wp_enqueue_style( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'formipay-form', FORMIPAY_URL . 'public/assets/css/form-style.css', [], FORMIPAY_VERSION, 'all');
wp_enqueue_style( 'formipay-popup', FORMIPAY_URL . 'public/assets/css/popup-style.css', [], FORMIPAY_VERSION, 'all');
}
public function public_enqueue_scripts() {
wp_enqueue_script( 'jquery-blockui', FORMIPAY_URL . 'vendor/jQuery-UI/jquery.blockUI.min.js', ['jquery'], FORMIPAY_VERSION, true);
wp_enqueue_script( 'jquery-clipboard', FORMIPAY_URL . 'vendor/ClipboardJS/clipboard.min.js', ['jquery'], '2.0.11', true);
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.5', true);
wp_enqueue_script( 'jquery-modal', FORMIPAY_URL . 'vendor/jQueryModal/jquery.modal.min.js', ['jquery'], '0.9.1', true);
wp_enqueue_script( 'choices', FORMIPAY_URL . 'vendor/ChoicesJS/choices.min.js', [], FORMIPAY_VERSION, true );
wp_enqueue_script( 'formipay-popup', FORMIPAY_URL . 'public/assets/js/popup-action.js', ['jquery', 'choices'], FORMIPAY_VERSION, true);
wp_enqueue_script( 'formipay-form', FORMIPAY_URL . 'public/assets/js/form-action.js', ['jquery', 'choices'], FORMIPAY_VERSION, true);
// Localize data for all forms
$form_data = [
'ajax_url' => admin_url('admin-ajax.php'),
'frontend_nonce' => wp_create_nonce('formipay-frontend-nonce'),
'nonce' => wp_create_nonce('formipay_order_submit'),
'forms' => $this->get_form_data()
];
wp_localize_script('formipay-form', 'formipay_form', $form_data);
}
private function get_form_data(){
$form_data = [];
foreach (array_unique(self::$form_ids) as $post_id) {
$form_data[$post_id] = [
'form_id' => $post_id,
'currency' => formipay_post_currency($post_id),
'buyer_phone_field' => formipay_get_post_meta($post_id, 'buyer_phone'),
'buyer_country_field' => formipay_get_post_meta($post_id, 'buyer_country'),
'buyer_phone_allow' => (bool) formipay_get_post_meta($post_id, 'buyer_allow_choose_country_code'),
'buyer_phone_country_code' => formipay_get_post_meta($post_id, 'buyer_phone_country_code'),
'decimal_digits' => formipay_get_post_meta($post_id, 'product_currency_decimal_digits'),
'decimal_symbol' => formipay_get_post_meta($post_id, 'product_currency_decimal_symbol'),
'thousand_separator' => formipay_get_post_meta($post_id, 'product_currency_thousand_separator'),
'notice_empty_text_message' => formipay_get_post_meta($post_id, 'empty_required_text_field'),
'notice_empty_select_message' => formipay_get_post_meta($post_id, 'empty_required_select_field'),
'notice_empty_agreement_message' => formipay_get_post_meta($post_id, 'empty_required_agreement_field'),
'quantity_toggle' => formipay_get_post_meta($post_id, 'product_quantity_toggle'),
'quantity_step' => formipay_get_post_meta($post_id, 'product_quantity_range'),
'quantity_min' => formipay_get_post_meta($post_id, 'product_minimum_purchase'),
'quantity_stock' => formipay_get_post_meta($post_id, 'product_stock'),
'button_text' => formipay_get_post_meta($post_id, 'button_text'),
'button_processing_text' => formipay_get_post_meta($post_id, 'button_processing_text'),
'isPopup' => formipay_isPopup($post_id),
'trigger_selector' => formipay_get_post_meta($post_id, 'popup_click_selector') ?
formipay_get_post_meta($post_id, 'popup_trigger_selector') :
'.formipay-open-popup-button',
'modal_selector' => '#formipay-popup-' . $post_id
];
}
return $form_data;
}
}
?>

219
includes/Settings.php Normal file
View File

@@ -0,0 +1,219 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Settings {
use SingletonTrait;
/**
* Initializes the plugin by setting filters and administration functions.
*/
protected function __construct() {
add_filter( 'wpcfto_options_page_setup', [$this, 'theme_option'] );
add_action( 'admin_enqueue_scripts', [$this, 'enqueue'] );
}
public function theme_option($setups){
$general_fields = array(
'business_group' => array(
'type' => 'group_title',
'label' => __( 'Business', 'formipay' ),
'group' => 'started'
),
'business_name' => array(
'type' => 'text',
'label' => __( 'Business Name', 'formipay' ),
'description' => __( 'This may be displayed on payment gateway like Paypal as a merchant name.', 'formipay' ),
'required' => true,
'group' => 'ended'
),
'currency_group' => array(
'type' => 'group_title',
'label' => __( 'Currency', 'formipay' ),
'group' => 'started'
),
'payment_default_currency' => array(
'type' => 'select',
'label' => __('Default Currency', 'formipay'),
'value' => 'IDR:::Indonesian rupiah:::Rp',
'options' => formipay_currency_as_options(),
'required' => true,
'searchable' => true,
),
'payment_default_currency_decimal_digits' => array(
'type' => 'number',
'label' => __('Decimal Digits', 'formipay'),
'value' => '2',
'required' => true,
),
'payment_default_currency_decimal_symbol' => array(
'type' => 'text',
'label' => __('Decimal Symbol', 'formipay'),
'value' => '.',
'required' => true,
),
'payment_default_currency_thousand_separator' => array(
'type' => 'text',
'label' => __('Thousand Separator Symbol', 'formipay'),
'value' => ',',
'required' => true,
'group' => 'ended'
),
);
$general_fields = apply_filters( 'formipay/global-settings/tab:general', $general_fields );
$pages_fields = array(
'thankyou_page_group' => array(
'type' => 'group_title',
'label' => __( 'Thank-You Page Style', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
'group' => 'started'
),
'thankyou_link' => array(
'type' => 'text',
'label' => __( 'Thank-You Page Link', 'formipay' ),
'value' => 'thankyou',
'submenu' => __( 'Thank-You Page', 'formipay' ),
'required' => true
),
'thankyou_style' => array(
'type' => 'image_select',
'label' => esc_html__( 'Style', 'formipay' ),
'width' => 100,
'height' => 100,
'value' => 'receipt',
'options' => array(
'card' => array(
'alt' => 'Card',
'img' => FORMIPAY_URL . 'admin/assets/img/thankyou_card_style.png'
),
'receipt' => array(
'alt' => 'Receipt',
'img' => FORMIPAY_URL . 'admin/assets/img/thankyou_receipt_style.png'
),
),
'submenu' => __( 'Thank-You Page', 'formipay' ),
'required' => true
),
'thankyou_page_container_bg_color' => array(
'type' => 'color',
'label' => __( 'Container Background Color', 'formipay' ),
'value' => '#808080',
'description' => __( 'Container is the main div on Thank-You Page contents', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
),
'thankyou_page_wrapper_bg_color' => array(
'type' => 'color',
'label' => __( 'Wrapper Background Color', 'formipay' ),
'value' => '#ffffff',
'description' => __( 'Wrapper is the div that fit to Thank-You Page contents width', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
),
'thankyou_page_wrapper_max_width' => array(
'type' => 'number',
'label' => __( 'Wrapper Max Width', 'formipay' ),
'value' => '600',
'submenu' => __( 'Thank-You Page', 'formipay' ),
'group' => 'ended',
'required' => true
),
'thankyou_page_restriction_group' => array(
'type' => 'group_title',
'label' => __( 'Restriction Access', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
'group' => 'started'
),
'thankyou_page_restriction_thumbnail' => array(
'type' => 'image',
'label' => __( 'Thumbnail', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' )
),
'thankyou_page_restriction_title' => array(
'type' => 'text',
'label' => __( 'Title', 'formipay' ),
'value' => __( 'Request to Access', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' )
),
'thankyou_page_restriction_message' => array(
'type' => 'hint_textarea',
'label' => __( 'Message', 'formipay' ),
'value' => __( 'Input your {{media}} to get new access link.', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
'hints' => array(
'media' => __( 'Contact Media', 'formipay' )
),
'description' => __( 'Use {{media}} shortcode to define what media of contact the buyer can receive the access link.', 'formipay' )
),
'thankyou_page_restriction_button' => array(
'type' => 'text',
'label' => __( 'Request Access Button', 'formipay' ),
'value' => __( 'Get Access Link', 'formipay' ),
'submenu' => __( 'Thank-You Page', 'formipay' ),
'group' => 'ended'
)
);
$pages_fields = apply_filters( 'formipay/global-settings/tab:pages', $pages_fields );
$global = array(
'General' => array(
'name' => __( 'General', 'formipay' ),
'fields' => $general_fields
),
'Pages' => array(
'name' => __( 'Pages', 'formipay' ),
'fields' => $pages_fields
)
);
$global = apply_filters( 'formipay/global-settings', $global );
foreach($global as $key => $value){
$fields[$key] = $value;
}
$setups[] = array(
'option_name' => 'formipay_settings',
'title' => __('Formipay', 'formipay'),
'sub_title' => __('Settings', 'formipay'),
'logo' => FORMIPAY_URL . 'admin/assets/img/formipay-logo-circle-white.png',
'page' => array(
'parent_slug' => 'formipay',
'page_title' => __('Formipay Settings', 'formipay'),
'menu_title' => __('Settings', 'formipay'),
'menu_slug' => 'formipay-settings',
'position' => 40,
),
'fields' => $fields
);
return $setups;
}
public function enqueue() {
global $current_screen;
if ( $current_screen->id === 'formipay_page_formipay-settings' ) {
wp_enqueue_style('admin-setting-style', FORMIPAY_URL . 'admin/assets/css/global-setting.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script('admin-setting-script', FORMIPAY_URL . 'admin/assets/js/admin-setting.js', ['jquery'], FORMIPAY_VERSION, true);
wp_localize_script( 'admin-setting-script', 'formipay_admin_setting', [
'ajax_url' => admin_url('admin-ajax.php'),
'site_url' => site_url(),
'nonce' => wp_create_nonce('formipay-admin-nonce')
] );
}
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace Formipay\Shipping;
use Formipay\Traits\SingletonTrait;
use Formipay\Shipping\Shipping;
if ( ! defined( 'ABSPATH' ) ) exit;
class FlatRate extends Shipping {
use SingletonTrait;
private $shipping_method = 'flat_rate';
protected function __construct() {
parent::__construct();
add_filter( 'formipay/product-config/tab:shipping/method', [$this, 'add_shipping_method'], 15 );
add_filter( 'formipay/product-config/tab:shipping', [$this, 'add_shipping_settings'], 15 );
// Add to order details
add_filter( 'formipay/order/order-details', [$this, 'add_shipping_to_order_details'], 99, 3 );
}
public function add_shipping_method($shipping_methods){
$shipping_methods['flat_rate'] = [
'method' => __( 'Flat Rate', 'formipay' ),
];
return $shipping_methods;
}
public function add_shipping_settings($fields) {
$flat_rate_fields = array(
$this->shipping_method.'_group' => array(
'type' => 'group_title',
'label' => __( 'Flat Rate Setup', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
'group' => 'started'
),
$this->shipping_method.'_type' => array(
'type' => 'select',
'label' => __( 'Type', 'formipay' ),
'options' => array(
'fixed' => __( 'Fixed', 'formipay' ),
'percentage' => __( 'Percentage', 'formipay' )
),
'value' => 'fixed',
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
),
$this->shipping_method.'_amount' => array(
'type' => 'number',
'label' => __( 'Amount', 'formipay' ),
'value' => '10',
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
),
$this->shipping_method.'_label' => array(
'type' => 'text',
'label' => __( 'Label', 'formipay' ),
'description' => __( 'This will be shown in Order Review and Order Details', 'formipay' ),
'value' => __( 'Shipping Fee', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'flat_rate'
)
),
'dependencies' => '&&',
'group' => 'ended'
),
);
foreach($flat_rate_fields as $key => $value){
$fields[$key] = $value;
}
return $fields;
}
public function add_shipping_to_order_details( $details, $form_id, $order_data ) {
if( formipay_get_post_meta($form_id, 'product_type') == 'physical' && formipay_get_post_meta($form_id, 'shipping_method')){
$amount = floatval( formipay_price_format( formipay_get_post_meta( $form_id, 'flat_rate_amount' ) ) );
if( formipay_get_post_meta($form_id, 'flat_rate_type') == 'percentage' ) {
$price = floatval( formipay_get_post_meta($form_id, 'product_price') );
$calculate = $price * $amount / 100;
$amount = floatval($calculate);
}
$details[] = [
'item' => formipay_get_post_meta($form_id, 'flat_rate_label'),
'amount' => $amount,
'subtotal' => $amount
];
}
return $details;
}
}

View File

@@ -0,0 +1,160 @@
<?php
namespace Formipay\Shipping;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
abstract class Shipping {
use SingletonTrait;
abstract public function add_shipping_method($shipping_methods);
abstract public function add_shipping_settings($fields);
abstract public function add_shipping_to_order_details($details, $form_id, $order_data);
protected function __construct() {
add_filter( 'formipay/global-settings', [$this, 'add_setting_shipping_menu'], 15 );
add_filter( 'formipay/product-config', [$this, 'add_form_shipping_menu'], 75 );
}
public function add_setting_shipping_menu($fields){
$shipping_settings = [];
$shipping_settings = apply_filters( 'formipay/global-settings/tab:shipping', $shipping_settings );
if(!empty($shipping_settings)){
$fields['shipping'] = array(
'name' => __('Shipping', 'formipay'),
'fields' => $shipping_settings
);
}
return $fields;
}
public function add_form_shipping_menu($fields) {
$shipping_methods = apply_filters( 'formipay/product-settings/tab:shipping/method', [
'free_shipping' => [
'method' => __( 'Free Shipping', 'formipay' )
]
] );
$shipping_options = [];
$shipping_fields = [];
foreach($shipping_methods as $id => $shipping){
// $id = $shipping['id'];
$label = $shipping['method'];
if(isset($shipping['courier'])){
$label .= ' - '.$shipping['courier'];
if(isset($shipping['service'])){
$label .= ' - '.$shipping['service'];
}
}
$shipping_options[$id] = $label;
}
$shipping_fields = [
'shipping_notice' => array(
'type' => 'notification_message',
'image' => FORMIPAY_URL . 'admin/assets/img/logistics.png',
'description' => __( '
<h1>No Shipping Method Available</h1>
<p>Shipping methods only for physical product type. If you insist to use shipping method, change your product type first</p>
', 'formipay' ),
'dependency' => array(
'key' => 'product_type',
'value' => 'digital',
'section' => 'general'
),
),
'shipping_method' => array(
'type' => 'radio',
'label' => esc_html__('Shipping Methods', 'formipay'),
'options' => $shipping_options,
'dependency' => array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
)
];
$free_shipping_fields = array(
'free_shipping_group' => array(
'type' => 'group_title',
'label' => __( 'Free Shipping Setup', 'formipay' ),
'description' => __( 'Will not add any shipping fee to the order', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'free_shipping'
)
),
'dependencies' => '&&',
'group' => 'started'
),
'free_shipping_label' => array(
'type' => 'text',
'label' => __( 'Label', 'formipay' ),
'value' => __( 'Free Shipping', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'free_shipping'
)
),
'dependencies' => '&&',
),
'free_shipping_add_to_order_review' => array(
'type' => 'checkbox',
'label' => __( 'Show in Order Review', 'formipay' ),
'dependency' => array(
array(
'key' => 'product_type',
'value' => 'physical',
'section' => 'general'
),
array(
'key' => 'shipping_method',
'value' => 'free_shipping'
)
),
'dependencies' => '&&',
'group' => 'ended'
),
);
foreach($free_shipping_fields as $key => $value) {
$shipping_fields[$key] = $value;
}
$shipping_fields = apply_filters( 'formipay/product-settings/tab:shipping', $shipping_fields );
if(!empty($shipping_fields)){
$fields['formipay_product_settings']['shipping'] = array(
'name' => __( 'Shipping', 'formipay' ),
'fields' => $shipping_fields
);
}
return $fields;
}
}

695
includes/Thankyou.php Normal file
View File

@@ -0,0 +1,695 @@
<?php
namespace Formipay;
use Formipay\Traits\SingletonTrait;
if ( ! defined( 'ABSPATH' ) ) exit;
class Thankyou {
use SingletonTrait;
private $form_id;
private $order_data;
private $order_details;
private $form_submit;
protected function __construct() {
add_action( 'init', [$this, 'set_endpoint'], 999);
add_filter( 'query_vars', [$this, 'set_query_vars'], 899);
add_action( 'wp_enqueue_scripts', [$this, 'frontend_enqueue'] );
add_filter( 'pre_get_document_title', [$this, 'page_title'], 1012 );
add_action( 'template_redirect', [$this, 'check_parse_query'], 999 );
add_action( 'wp_ajax_request_access_link', [$this, 'request_access_link'] );
add_action( 'wp_ajax_nopriv_request_access_link', [$this, 'request_access_link'] );
}
public function set_endpoint() {
$slug = 'thankyou';
$formipay_settings = get_option('formipay_settings');
if(!empty($formipay_settings['thankyou_link'])){
$slug = $formipay_settings['thankyou_link'];
}
add_rewrite_rule(
'^'.$slug.'/([^/]*)/?',
'index.php?formipay-thankyou=1&formipay-token=$matches[1]',
'top'
);
flush_rewrite_rules();
}
public function set_query_vars($vars){
$vars[] = 'formipay-thankyou';
$vars[] = 'formipay-token';
return $vars;
}
public function check_parse_query() {
global $wp_query;
$formipay_settings = get_option('formipay_settings');
if(is_admin()) :
return;
endif;
if(
!is_admin() &&
is_array($wp_query->query) &&
array_key_exists('formipay-thankyou', $wp_query->query) &&
true === boolval($wp_query->query['formipay-thankyou'])
) :
$token_value = sanitize_text_field($wp_query->query['formipay-token']);
$token_manager = new \Formipay\Token();
// New token validation
if ($token_data = $token_manager->validate($token_value)) {
$this->form_id = $form_id = $token_data['form_id'];
$this->order_id = $order_id = $token_data['order_id'];
$token_manager->increment_usage($token_value);
}
else {
wp_die('Invalid access token');
}
$this->order = $order = formipay_get_order($order_id);
$this->order_details = $order['items'];
$this->form_submit = $order['form_data'];
$button_background_color = json_decode(formipay_get_post_meta($form_id, 'button_bg_color'), true );
$button_text_color = json_decode(formipay_get_post_meta($form_id, 'button_text_color'), true );
$button_border_color = json_decode( formipay_get_post_meta($form_id, 'button_border_color'), true );
include_once FORMIPAY_PATH . 'public/templates/thank-you.php';
exit;
endif;
}
public function process_order_meta() {
$order_meta = [];
if(!empty($this->order['meta_data'])){
foreach($this->order['meta_data'] as $meta_data){
$key = $meta_data['name'];
$value = $meta_data['value'];
$order_meta[$key] = $value;
}
}
return $order_meta;
}
public function access_method() {
$order_meta = $this->process_order_meta();
$access_method = 'magic_link';
if(isset($order_meta['access_method'])) {
$access_method = $order_meta['access_method'];
}
return $access_method;
}
public function frontend_enqueue() {
global $wp_query;
if(
is_array($wp_query->query) &&
array_key_exists('formipay-thankyou', $wp_query->query) &&
true === boolval($wp_query->query['formipay-thankyou'])
) :
wp_enqueue_style( 'bs-icons', FORMIPAY_URL . 'vendor/Bootstrap/bootstrap-icons.css', [], '1.11.1', 'all');
wp_enqueue_style( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.css', [], '11.14.5', 'all');
wp_enqueue_style( 'formipay-form-style', FORMIPAY_URL . 'public/assets/css/form-style.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_style( 'formipay-thankyou', FORMIPAY_URL . 'public/assets/css/thankyou.css', [], FORMIPAY_VERSION, 'all' );
wp_enqueue_script( 'jquery-blockui', FORMIPAY_URL . 'vendor/jQuery-UI/jquery.blockUI.min.js', ['jquery'], '2.7.0', true);
wp_enqueue_script( 'jquery-clipboard', FORMIPAY_URL . 'vendor/ClipboardJS/clipboard.min.js', ['jquery'], '2.0.11', true);
wp_enqueue_script( 'sweetalert2', FORMIPAY_URL . 'vendor/SweetAlert2/sweetalert2.min.js', ['jquery'], '11.14.5', true);
wp_enqueue_script( 'formipay-thankyou', FORMIPAY_URL . 'public/assets/js/thankyou.js', ['jquery'], FORMIPAY_VERSION, true );
wp_enqueue_script( 'formipay-access-request', FORMIPAY_URL . 'public/assets/js/access-link-request.js', ['jquery'], FORMIPAY_VERSION, true );
$order_id = $this->order['id'];
wp_localize_script( 'formipay-thankyou', 'formipay_thankyou', [
'ajax_url' => admin_url('admin-ajax.php'),
'order_id' => $this->order_id,
'access_method' => $this->access_method(),
'email_validation' => [
'error' => [
'message' => __( 'Input a valid email address', 'formipay' )
]
],
'nonce' => wp_create_nonce('formipay-thankyou-nonce')
] );
endif;
}
public function can_access() {
$order_id = $this->order['id'];
// $order_meta = $this->order['meta_data'];
$order_meta = $this->process_order_meta();
$order_ip_address = $order_meta['ip_address'];
$order_user_agent = $order_meta['user_agent'];
$order_device_type = $order_meta['device_type'];
$current_ip_address = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'])) : '';
$current_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
$current_device_type = (isset($_SERVER['HTTP_USER_AGENT']) && strpos(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])), 'Mobile') !== false) ? 'Mobile' : 'Desktop';
$can_access = false;
if(
$order_ip_address === $current_ip_address &&
$order_user_agent === $current_user_agent &&
$order_device_type === $current_device_type
) {
$cookie = $this->get_cookie();
if(
!empty($order_meta['session_id']) &&
!empty($cookie) &&
isset($cookie[$order_id]) && $cookie[$order_id] == $order_meta['session_id']
){
$can_access = true;
}
}
return $can_access;
}
public function render_view() {
$thankyou_content = formipay_get_post_meta($this->form_id, 'thankyou_screen_content');
$formipay_order_statuses = formipay_order_status_list();
$this_order_status = $formipay_order_statuses[ $this->order['status']];
$buyer_name = '';
if(!empty(formipay_get_post_meta($this->form_id, 'buyer_name'))){
$buyer_name_field = formipay_get_post_meta($this->form_id, 'buyer_name');
if(!empty($this->order['form_data'])){
foreach($this->order['form_data'] as $_form_data){
if($_form_data['name'] == $buyer_name_field){
$buyer_name = $_form_data['value'];
break;
}
}
}
}
$shortcodes = [
'buyer_name' => $buyer_name,
'product_name' => get_the_title($this->form_id),
'order_id' => $this->order['id'],
'order_date' => $this->order['created_date'],
'order_total' => '<p data-copy-value="' . $this->order['total'] . '" style="margin-bottom: .25em;">' . formipay_price_format( $this->order['total'], $this->form_id ) . '</p>
<button class="formipay-copy-button" data-copy-text="Copy" data-copied-text="Copied" data-not-copied-text="Not copied!" style="font-size: 14px;">
<i class="bi bi-copy"></i>Copy
</button>',
'order_status' => $this_order_status,
'order_details' => $this->render_order_details(),
'form_submission' => $this->render_form_submit()
];
$shortcodes = apply_filters( 'formipay/thankyou/shortcodes', $shortcodes, $this->form_id, $this->order, 'thankyou' );
$replacements = [];
if(!empty($shortcodes)){
foreach($shortcodes as $key => $value){
$replacements['{{'.$key.'}}'] = $value;
}
}
$thankyou_content = strtr($thankyou_content, $replacements);
return $thankyou_content;
}
public function view_access() {
ob_start();
?>
<main class="formipay-thank-you" role="main">
<section class="formipay-content-wrapper receipt" aria-label="Order Receipt">
<div class="formipay-the-contents">
<?php echo wp_kses($this->render_view(), formipay_thankyoupage_allowed_html()); ?>
</div>
</section>
<?php if($this->access_items() !== '') { ?>
<section class="formipay-content-wrapper receipt downloads" aria-label="Downloads">
<div class="formipay-the-contents">
<?php echo $this->can_access() ? $this->access_items() : wp_kses($this->request_access(), formipay_thankyoupage_allowed_html()); ?>
</div>
</section>
<?php } ?>
</main>
<?php
$content = ob_get_contents();
ob_end_clean();
// echo $content;
echo wp_kses($content, formipay_thankyoupage_allowed_html());
// echo wp_kses_post( $content );
}
public function request_access() {
$formipay_settings = get_option('formipay_settings');
if($this->order['status'] !== 'completed') {
return;
}
$active_media = [];
if( formipay_get_post_meta($this->form_id, 'product_access_to_email') == 'on'){
$active_media[] = 'email';
}
$active_media = apply_filters('formipay/order/access/active-media', $active_media, $this->form_id);
ob_start();
?>
<div class="request-thumbnail">
<?php
echo wp_get_attachment_image( $formipay_settings['thankyou_page_restriction_thumbnail'], 'full', false, [
'width' => '120'
] );
?>
</div>
<h2 id="access-request-heading"><?php echo esc_html($formipay_settings['thankyou_page_restriction_title']); ?></h2>
<form action="" class="request-for-access">
<div class="formipay-field-group" style="margin-bottom: 1em;">
<?php if($this->access_method() == 'magic_link') { ?>
<input type="text" name="access-pass-request" id="access-pass"
class="formipay-input"
value="" placeholder="Input <?php echo esc_html( implode('/', $active_media) ); ?>" required>
<p>
<?php echo
esc_html(
str_replace('{{media}}', implode(',', $active_media) ,$formipay_settings['thankyou_page_restriction_message'])
);
?>
</p>
<?php } elseif($this->access_method() == 'static_password') { ?>
<input type="password" name="access_password" id="access-pass"
class="formipay-input"
value="" placeholder="Input <?php echo esc_html( 'Password' ); ?>" required>
<?php } ?>
</div>
<button type="submit" id="request_access_to_media" class="formipay-submit-button" disabled>
<?php echo esc_html( $formipay_settings['thankyou_page_restriction_button'] ); ?>
</button>
</form>
<?php
$content = ob_get_contents();
ob_end_clean();
return $content;
}
public function request_access_link() {
check_ajax_referer( 'formipay-thankyou-nonce', '_wpnonce' );
$pass = isset($_REQUEST['pass']) ? sanitize_text_field( wp_unslash($_REQUEST['pass']) ) : '';
$order_id = isset($_REQUEST['order']) ? intval($_REQUEST['order']) : 0;
$method = isset($_REQUEST['method']) ? sanitize_text_field( wp_unslash($_REQUEST['method']) ) : '';
$order = formipay_get_order($order_id);
$formipay_order = new Formipay_Order();
$formipay_get_order = $formipay_order->get($order_id);
$order_meta_data = maybe_unserialize($formipay_get_order->meta_data);
$valid = false;
// validate pass
if($method == 'magic_link'){
$email_field_name = formipay_get_post_meta($order['form_id'], 'notification_email_buyer_recipient');
// $email_purchase = $order['form_data'][$email_field_name];
$email_purchase = '';
foreach($order['form_data'] as $form_data){
if($form_data['name'] == $email_field_name){
$email_purchase = $form_data['value'];
break;
}
}
if($email_purchase !== $pass){
wp_send_json( [
'title' => __( 'Wrong email!', 'formipay' ),
'message' => __( 'You must enter an email address that matches the email you used during purchase.', 'formipay' ),
'icon' => 'error'
] );
}
$valid = true;
}
if($method == 'static_password'){
if(
!empty($order_meta_data['access_password']) &&
$pass !== $order_meta_data['access_password']
) {
wp_send_json([
'title' => __( 'Password is invalid', 'formipay' ),
'message' => __( 'Please input a valid password to access this page.', 'formipay' ),
'icon' => 'error'
]);
}
$valid = true;
}
if(!$valid){
wp_send_json([
'title' => __( 'Not provided access method', 'formipay' ),
'message' => __( 'Contact the web master for further information or try again.', 'formipay' ),
'icon' => 'error'
]);
}
// $order_meta_data = $order['meta_data'];
$user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '';
$order_meta_data['session_id'] = uniqid();
$order_meta_data['ip_address'] = isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field( wp_unslash($_SERVER['REMOTE_ADDR']) ) : '';
$order_meta_data['user_agent'] = $user_agent;
$order_meta_data['device_type'] = (strpos( $user_agent, 'Mobile') !== false) ? 'Mobile' : 'Desktop';
$update = $formipay_order->update(
$order['id'],
[
'meta_data' => $order_meta_data
]
);
if(is_wp_error( $update )){
wp_send_json( [
'title' => __( 'Failed!', 'formipay' ),
'message' => __( 'Something\'s happened. Contact the web master for further information or try again.', 'formipay' ),
'icon' => 'error'
] );
}
$cookie = $this->get_cookie();
$cookie[$order_id] = $order_meta_data['session_id'];
setcookie( 'fp_access', maybe_serialize($cookie), time() + 86400, '/' );
if($method == 'magic_link'){
do_action('formipay/notification/access/email', $order);
wp_send_json( [
'title' => __( 'Access Link Sent!', 'formipay' ),
'message' => __( 'Check your Inbox (or maybe Spam) to find a new access link so you can get digital access of this purchase.', 'formipay' ),
'icon' => 'success',
'action' => 'idle'
] );
}elseif($method == 'static_password') {
wp_send_json( [
'title' => __( 'Password is Valid!', 'formipay' ),
'message' => __( 'You will be redirected to new link. Please wait.', 'formipay' ),
'icon' => 'success',
'action' => 'reload'
] );
}
}
public function access_items() {
$content = '';
if($this->order['status'] == 'completed' && !empty( formipay_get_post_meta($this->form_id, 'product_accesses') ) ){
// $downloads = json_decode( formipay_get_post_meta($this->form_id, 'product_access_downloads'), true );
$accesses = explode(',', formipay_get_post_meta($this->form_id, 'product_accesses'));
if(!empty($accesses)){
ob_start();
foreach($accesses as $access){
$type = formipay_get_post_meta( $access, 'access_type' );
$button_text = formipay_get_post_meta( $access, 'button_text' );
if($type == 'download'){
$source = formipay_get_post_meta( $access, 'access_download_source' );
if($source == 'external'){
$link = formipay_get_post_meta( $access, 'access_url' );
$filesize = formipay_get_post_meta( $access, 'details_filesize' );
}elseif($source == 'wp_media'){
$attachment_id = formipay_get_post_meta( $access, 'access_attachment' );
$link = wp_get_attachment_url($attachment_id);
$filesize = size_format( filesize( get_attached_file( $attachment_id ) ), 2 );
}
}elseif($type == 'redirect'){
$link = formipay_get_post_meta( $access, 'access_redirect_url' );
$attachment_id = 0;
$filesize = '';
}
$image = formipay_attachment_icon($attachment_id);
if(!empty(formipay_get_post_meta($access, 'details_icon'))){
$image = wp_get_attachment_image(formipay_get_post_meta($access, 'details_icon'), 'thumbnail', false, [
'width' => '36',
'height' => '36'
]);
}
?>
<div class="formipay-single-download">
<?php echo wp_kses_post($image); ?>
<div class="formipay-download-information">
<div class="formipay-file-info">
<span class="formipay-file-title">
<?php echo esc_html(get_the_title( $access )); ?>
</span>
<?php if(false !== formipay_get_post_meta($access, 'details_short_description')) { ?>
<p class="formipay-access-item-description">
<?php echo esc_html(formipay_get_post_meta($access, 'details_short_description')); ?>
</p>
<?php } ?>
<?php if( '' !== $filesize){ ?>
<span class="formipay-file-size">
<?php echo esc_html($filesize); ?>
</span>
<?php } ?>
</div>
<a id="formipay-download-<?php echo esc_attr($access) ?>" class="formipay-download-button formipay-submit-button" href="<?php echo esc_url($link) ?>" <?php echo $type == 'download' ? 'download' : 'target="_blank"' ?>>
<?php echo esc_html($button_text); ?>
</a>
</div>
</div>
<?php
}
$content = '<div class="formipay-downloads">'.ob_get_contents().'</div>';
ob_end_clean();
}
}
return $content;
}
public function page_title($title) {
global $wp_query;
if(
is_array($wp_query->query) &&
array_key_exists('formipay-thankyou', $wp_query->query) &&
true === boolval($wp_query->query['formipay-thankyou'])
) :
$token = explode(':::', base64_decode($wp_query->query['formipay-token']));
$form_id = $token[0];
$order_id = $token[1];
$title = 'Thankyou' . ' for your order #' . $order_id;
endif;
return $title;
}
public function render_order_details() {
ob_start();
if(!empty($this->order_details)){
?>
<table id="order-details">
<tbody>
<?php
foreach($this->order_details as $detail){
$qty = '';
if(isset($detail['qty'])){
$qty = ' <i class="bi bi-x"></i> '.$detail['qty'];
}
?>
<tr>
<th><?php echo wp_kses_post($detail['item']) . wp_kses_post($qty); ?></th>
<td><?php echo esc_html(formipay_price_format($detail['subtotal'], $this->form_id)); ?></td>
</tr>
<?php
}
?>
<tr>
<th><?php echo esc_html__( 'Total', 'formipay' ); ?></th>
<td><b><?php echo esc_html(formipay_price_format($this->order['total'], $this->form_id)); ?></b></td>
</tr>
</tbody>
</table>
<?php
}
$content = ob_get_contents();
ob_get_clean();
return $content;
}
public function render_form_submit() {
ob_start();
if(!empty($this->form_submit)){
$field_config = formipay_get_post_meta($this->form_id, 'formipay_settings');
?>
<table id="order-submission-details">
<tbody>
<tr>
<th><?php echo esc_html__('Product', 'formipay'); ?></th>
<td><?php echo esc_html(get_the_title($this->form_id)); ?></td>
</tr>
<?php
$skip_row = array(
'form_id', 'payment', 'order_id', 'order_status', 'grand_total', 'order_date'
);
foreach($this->form_submit as $field){
// $field_label = $field_config['fields'][$field_name.'_config']['label'];
$field_name = $field['name'];
$field_value = $field['value'];
if(in_array($field_name, $skip_row)){
continue;
}
if(empty($field_value)){
continue;
}
switch ($field_name) {
case 'form_id':
$field_label = esc_html__( 'Form ID', 'formipay' );
break;
case 'qty':
$field_label = esc_html__( 'Quantity', 'formipay' );
break;
case 'coupon_code':
$field_label = esc_html__( 'Coupon Discount', 'formipay' );
break;
case 'payment':
$field_label = esc_html__( 'Payment', 'formipay' );
break;
case 'payment_gateway':
$field_label = esc_html__( 'Payment Method', 'formipay' );
// $field_value = ucwords(str_replace('_', ' ', $field_value));
$field_value = $field_value == 'cod' ? 'Cash on Delivery' : ucwords( str_replace(['-','_'], ' ', $field_value) );
break;
case 'order_id':
$field_label = esc_html__( 'Order ID', 'formipay' );
break;
case 'grand_total':
$field_label = esc_html__( 'Grand Total', 'formipay' );
$field_value = esc_html(formipay_price_format($field_value, $this->form_id));
break;
case 'order_status':
$field_label = esc_html__( 'Order Status', 'formipay' );
break;
case 'order_date':
$field_label = esc_html__( 'Order Date', 'formipay' );
break;
default:
// $field_label = $field_config['fields'][$field_name.'_config']['label'];
if(is_array($field_value)){
$field_value = $field_value['value'];
}
if(filter_var($field_value, FILTER_VALIDATE_EMAIL)){
$field_value = $field_value;
}elseif(strpos($field_value, ',') !== false){
$values = explode(',', $field_value);
$field_value = '';
foreach($values as $value){
$field_value .= '<span class="checkbox-span">'.$value.'</span> ';
}
}else{
$field_value = ucwords( str_replace(['-','_'], ' ', $field_value) );
}
break;
}
?>
<tr>
<th><?php echo esc_html($field_label) ?></th>
<td><?php echo wp_kses_post($field_value) ?></td>
</tr>
<?php
}
?>
</tbody>
</table>
<?php
}
$content = ob_get_contents();
ob_get_clean();
return $content;
}
public function get_cookie() {
$cookie = [];
if (!empty($_COOKIE['fp_access'])) {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$raw_value = wp_unslash($_COOKIE['fp_access']);
// Validate before unserializing
if (is_serialized($raw_value)) { // Use WordPress core function
$maybe = maybe_unserialize($raw_value);
// Type-check and sanitize array contents
if (is_array($maybe)) {
$cookie = array_map('sanitize_text_field', $maybe);
}
}
}
return $cookie;
}
}

155
includes/Token.php Normal file
View File

@@ -0,0 +1,155 @@
<?php
namespace Formipay;
use Exception;
if (!defined('ABSPATH')) exit;
class Token {
private $table_name;
public function __construct() {
global $wpdb;
$this->table_name = $wpdb->prefix . 'formipay_tokens';
add_action( 'init', [$this, 'create_db'] );
// Setup cleanup hook
add_action( 'formipay_daily_cleanup', [$this, 'cleanup_expired_tokens']);
if (!wp_next_scheduled('formipay_daily_cleanup')) {
wp_schedule_event(time(), 'daily', 'formipay_daily_cleanup');
}
}
public function create_db() {
global $wpdb;
$table_name = $wpdb->prefix . 'formipay_tokens';
$sql = "CREATE TABLE $table_name (
token CHAR(64) NOT NULL,
form_id BIGINT UNSIGNED NOT NULL,
order_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL,
used TINYINT(1) NOT NULL DEFAULT 0,
use_count INT NOT NULL DEFAULT 0,
PRIMARY KEY (token),
KEY expires_at_idx (expires_at),
KEY order_id_idx (order_id)
) {$wpdb->get_charset_collate()};";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
/**
* Generate cryptographically secure token
*
* @param int $order_id
* @param int $form_id
* @param int $expires_in Seconds until expiration (default: 900 = 15 mins)
* @return string
*/
public function generate(int $order_id, int $form_id, int $expires_in = 900): string {
global $wpdb;
$token = bin2hex(random_bytes(32)); // 64-character token
$expires_at = formipay_date('Y-m-d H:i:s', time() + $expires_in);
$wpdb->insert($this->table_name, [
'token' => $token,
'form_id' => $form_id,
'order_id' => $order_id,
'expires_at' => $expires_at,
'created_at' => formipay_date('Y-m-d H:i:s')
]);
return $token;
}
/**
* Validate token and retrieve order data
*
* @param string $token
* @return array|false
*/
public function validate(string $token) {
global $wpdb;
// Validate token format
if (!preg_match('/^[a-f0-9]{64}$/', $token)) return false;
$query = $wpdb->prepare(
"SELECT form_id, order_id
FROM {$this->table_name}
WHERE token = %s
AND expires_at > NOW()
AND used = 0",
$token
);
return $wpdb->get_row($query, ARRAY_A);
}
/**
* Mark token as used
*
* @param string $token
* @return bool
*/
public function mark_used(string $token): bool {
global $wpdb;
return (bool) $wpdb->update(
$this->table_name,
['used' => 1],
['token' => $token]
);
}
/**
* Cleanup expired tokens
*/
public function cleanup_expired_tokens() {
global $wpdb;
$wpdb->query("DELETE FROM {$this->table_name} WHERE expires_at < NOW()");
}
/**
* Get token usage count
*
* @param string $token
* @return int
*/
public function get_usage_count(string $token): int {
global $wpdb;
return (int) $wpdb->get_var($wpdb->prepare(
"SELECT use_count FROM {$this->table_name} WHERE token = %s",
$token
));
}
/**
* Increment token usage
*
* @param string $token
* @param int $max_uses
* @return bool
* @throws Exception
*/
public function increment_usage(string $token, int $max_uses = 5): bool {
global $wpdb;
$current_count = $this->get_usage_count($token);
if ($current_count >= $max_uses) {
throw new Exception('Token usage limit reached');
}
return (bool) $wpdb->update(
$this->table_name,
['use_count' => $current_count + 1],
['token' => $token]
);
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace Formipay\Traits;
trait SingletonTrait {
private static $instance = null;
public static function get_instance() {
if (null === static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
private function __clone() {}
public function __wakeup() {}
}