<?php
|
namespace JVBase\rest\routes;
|
|
|
use JVBase\rest\RestRouteManager;
|
use WP_REST_Request;
|
use WP_REST_Response;
|
use Exception;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
|
class IntegrationsSquareRoutes extends RestRouteManager
|
{
|
public function registerRoutes():void
|
{
|
register_rest_route('jvb/v1/square', '/process-payment', [
|
'methods' => 'POST',
|
'callback' => [$this, 'handlePaymentProcessing'],
|
'permission_callback' => '__return_true' // Adjust based on your auth
|
]);
|
|
register_rest_route('jvb/v1/square', '/saved-cards', [
|
'methods' => 'GET',
|
'callback' => [$this, 'getSavedCards'],
|
'permission_callback' => 'is_user_logged_in'
|
]);
|
|
|
register_rest_route('jvb/v1/square', '/order-history', [
|
'methods' => 'GET',
|
'callback' => [$this, 'getOrderHistory'],
|
'permission_callback' => 'is_user_logged_in'
|
]);
|
|
|
register_rest_route('jvb/v1/square', '/order-status/(?P<order_id>[a-zA-Z0-9_-]+)', [
|
'methods' => 'GET',
|
'callback' => [$this, 'getOrderStatus'],
|
'permission_callback' => '__return_true' // Allow guests with order ID
|
]);
|
}
|
|
public function handlePaymentProcessing($request): array
|
{
|
$data = $request->get_json_params();
|
|
// Generate idempotency key from cart_id + timestamp
|
// This ensures retries use SAME key
|
$cart_id = $data['cart_id'] ?? '';
|
if (!$cart_id) {
|
return ['success' => false, 'message' => 'Missing cart ID'];
|
}
|
|
// Check if we already processed this cart
|
$existing_order = get_transient(BASE . 'cart_order_' . $cart_id);
|
if ($existing_order) {
|
// Return cached result - prevents double charge
|
return $existing_order;
|
}
|
|
// Generate idempotency key tied to this specific cart
|
$idempotency_key = 'cart_' . $cart_id . '_' . time();
|
|
// Store key to prevent reprocessing
|
set_transient(BASE . 'cart_idempotency_' . $cart_id, $idempotency_key, HOUR_IN_SECONDS);
|
|
// Validate required fields
|
$required = ['source_id', 'amount', 'items', 'customer'];
|
foreach ($required as $field) {
|
if (empty($data[$field])) {
|
return [
|
'success' => false,
|
'message' => "Missing required field: {$field}"
|
];
|
}
|
}
|
|
try {
|
$square = JVB()->connect('square');
|
// Step 1: Get or create Square customer
|
$customer_id = $square->getOrCreateSquareCustomer($data['customer']);
|
|
// Step 2: Create Order in Square
|
$order_response = $square->createSquareOrder($data['items'], $customer_id, $data);
|
|
if (is_wp_error($order_response)) {
|
throw new Exception($order_response->get_error_message());
|
}
|
|
$order_id = $order_response['order']['id'] ?? null;
|
if (!$order_id) {
|
throw new Exception('Failed to create Square order');
|
}
|
|
// Step 3: Create Payment in Square
|
$payment_response = $square->createSquarePayment(
|
$data['source_id'],
|
$data['idempotency_key'],
|
$data['amount'],
|
$order_id,
|
$customer_id
|
);
|
|
if (is_wp_error($payment_response)) {
|
throw new Exception($payment_response->get_error_message());
|
}
|
|
$payment = $payment_response['payment'] ?? null;
|
if (!$payment) {
|
throw new Exception('Failed to create Square payment');
|
}
|
|
// Step 4: Save order reference in WordPress
|
$wp_order_id = $square->saveOrderToWordPress([
|
'square_order_id' => $order_id,
|
'square_payment_id' => $payment['id'],
|
'customer' => $data['customer'],
|
'items' => $data['items'],
|
'amount' => $data['amount'],
|
'status' => $payment['status']
|
]);
|
|
$result = [
|
'success' => true,
|
'order_id' => $order_id,
|
'payment_id' => $payment['id'],
|
'wp_order_id' => $wp_order_id,
|
'status' => $payment['status'],
|
'receipt_url' => $payment['receipt_url'] ?? null
|
];
|
|
set_transient(BASE . 'cart_order_' . $cart_id, $result, HOUR_IN_SECONDS);
|
|
return $result;
|
|
} catch (Exception $e) {
|
$this->logError('Payment processing failed', [
|
'error' => $e->getMessage(),
|
'idempotency_key' => $data['idempotency_key']
|
]);
|
|
return [
|
'success' => false,
|
'message' => $e->getMessage()
|
];
|
}
|
}
|
|
public function getSavedCards($request): array
|
{
|
$user_id = get_current_user_id();
|
if (!$user_id) {
|
return ['success' => false, 'message' => 'Not logged in'];
|
}
|
|
$square = JVB()->connect('square');
|
|
// Get Square customer ID for this user
|
$square_customer_id = get_user_meta($user_id, BASE . '_square_customer_id', true);
|
|
if (!$square_customer_id) {
|
return ['success' => true, 'cards' => []];
|
}
|
|
// Fetch cards from Square (2025-compliant - separate endpoint)
|
$cards_response = $square->getRequest('cards?customer_id=' . $square_customer_id);
|
|
if (is_wp_error($cards_response)) {
|
return ['success' => false, 'message' => 'Failed to fetch cards'];
|
}
|
|
return [
|
'success' => true,
|
'cards' => $cards_response['cards'] ?? []
|
];
|
}
|
|
public function getOrderHistory($request): array
|
{
|
$user_id = get_current_user_id();
|
if (!$user_id) {
|
return ['success' => false, 'message' => 'Not logged in'];
|
}
|
|
// Get orders from custom post type
|
$orders = get_posts([
|
'post_type' => BASE . '_sq_orders',
|
'author' => $user_id,
|
'posts_per_page' => 50,
|
'orderby' => 'date',
|
'order' => 'DESC'
|
]);
|
|
$order_data = [];
|
foreach ($orders as $order) {
|
$meta = new \JVBase\meta\MetaManager($order->ID, 'post');
|
$order_data[] = [
|
'wp_order_id' => $order->ID,
|
'square_order_id' => $meta->getValue('square_order_id'),
|
'status' => $meta->getValue('status'),
|
'amount' => $meta->getValue('amount'),
|
'items' => $meta->getValue('items'),
|
'created_at' => $meta->getValue('created_at'),
|
'pickup_time' => $meta->getValue('pickup_time')
|
];
|
}
|
|
return [
|
'success' => true,
|
'orders' => $order_data
|
];
|
}
|
|
public function getOrderStatus($request): array
|
{
|
$order_id = $request->get_param('order_id');
|
|
// Find WP post by Square order ID
|
$wp_order_id = get_option(BASE . 'square_order_map_' . $order_id);
|
|
if (!$wp_order_id) {
|
return ['success' => false, 'message' => 'Order not found'];
|
}
|
|
$meta = new \JVBase\meta\MetaManager($wp_order_id, 'post');
|
|
return [
|
'success' => true,
|
'order' => [
|
'status' => $meta->getValue('status'),
|
'fulfillment_status' => $meta->getValue('fulfillment_status'),
|
'pickup_time' => $meta->getValue('pickup_time'),
|
'items' => $meta->getValue('items')
|
]
|
];
|
}
|
}
|