                    /**
                     * DCC Quote (GET /services/dcc/quote)
                     * @return array DCC quote result (masked)
                     */
                    public function actionDccQuote()
                    {
                        $request = Yii::$app->request;
                        $amount = $request->get('amount');
                        $currency = $request->get('currency');
                        $bin = $request->get('bin');
                        // Placeholder: Replace with actual DCC quote logic (API call)
                        if ($bin === '411111' && $currency === 'USD' && $amount == 100) {
                            $result = [
                                'bin' => '411111',
                                'amount' => 100,
                                'currency' => 'USD',
                                'converted_amount' => 92,
                                'converted_currency' => 'EUR',
                                'rate' => 0.92,
                                'rate_expiry' => '2026-03-14T00:00:00Z',
                            ];
                            return $this->maskSensitiveArray($result);
                        }
                        return ['error' => 'DCC quote not available'];
                    }
                /**
                 * BIN Lookup (GET /services/dcc/bin-check)
                 * @return array BIN lookup result (masked)
                 */
                public function actionBinCheck()
                {
                    $request = Yii::$app->request;
                    $bin = $request->get('bin');
                    // Placeholder: Replace with actual BIN lookup logic (API call)
                    if ($bin === '411111') {
                        $result = [
                            'bin' => '411111',
                            'brand' => 'Visa',
                            'country' => 'US',
                            'bank' => 'Demo Bank',
                            'type' => 'credit',
                            'currency' => 'USD',
                        ];
                        return $this->maskSensitiveArray($result);
                    }
                    return ['error' => 'BIN not found'];
                }
            /**
             * Delete payment method (DELETE /customers/{id}/payment-methods/{pmId})
             * @param string $id Customer ID
             * @param string $pmId Payment Method ID
             * @return array Deletion result (masked)
             */
            public function actionDeleteCustomerPaymentMethod($id, $pmId)
            {
                // Placeholder: Replace with actual delete logic (DB/API)
                if (($id === 'cust_001' || $id === 'cust_002') && ($pmId === 'pm_001' || $pmId === 'pm_002')) {
                    $result = [
                        'id' => $id,
                        'pmId' => $pmId,
                        'status' => 'deleted',
                        'message' => 'Payment method deleted successfully',
                    ];
                    return $this->maskSensitiveArray($result);
                }
                return ['error' => 'Payment method not found'];
            }
        /**
         * Retrieve payment method details (GET /customers/{id}/payment-methods/{pmId})
         * @param string $id Customer ID
         * @param string $pmId Payment Method ID
         * @return array Payment method details (masked)
         */
        public function actionCustomerPaymentMethodDetails($id, $pmId)
        {
            // Placeholder: Replace with actual payment method detail retrieval logic (DB/API)
            if (($id === 'cust_001' || $id === 'cust_002') && ($pmId === 'pm_001' || $pmId === 'pm_002')) {
                $method = null;
                if ($pmId === 'pm_001') {
                    $method = [
                        'pmId' => 'pm_001',
                        'type' => 'card',
                        'masked_card' => ($id === 'cust_001') ? '**** **** **** 1234' : '**** **** **** 5678',
                        'expiry' => '12/28',
                        'status' => 'active',
                    ];
                } elseif ($pmId === 'pm_002') {
                    $method = [
                        'pmId' => 'pm_002',
                        'type' => 'paypal',
                        'email' => ($id === 'cust_001') ? 'alice.paypal@example.com' : 'bob.paypal@example.com',
                        'status' => 'active',
                    ];
                }
                return $this->maskSensitiveArray($method);
            }
            return ['error' => 'Payment method not found'];
        }
    /**
     * Retrieve payment methods (GET /customers/{id}/payment-methods)
     * @param string $id Customer ID
     * @return array List of payment methods (masked)
     */
    public function actionCustomerPaymentMethods($id)
    {
        // Placeholder: Replace with actual payment method retrieval logic (DB/API)
        if ($id === 'cust_001' || $id === 'cust_002') {
            $methods = [
                [
                    'pmId' => 'pm_001',
                    'type' => 'card',
                    'masked_card' => ($id === 'cust_001') ? '**** **** **** 1234' : '**** **** **** 5678',
                    'expiry' => '12/28',
                    'status' => 'active',
                ],
                [
                    'pmId' => 'pm_002',
                    'type' => 'paypal',
                    'email' => ($id === 'cust_001') ? 'alice.paypal@example.com' : 'bob.paypal@example.com',
                    'status' => 'active',
                ],
            ];
            return $this->maskSensitiveArray($methods);
        }
        return ['error' => 'Customer not found'];
    }
<?php 
namespace PortalPeople\paygate\controllers\api;
    use yii\rest\Controller;
    use Yii;
    use PortalPeople\paygate\models\Payment;

    class PaymentsController extends Controller {
        /**
         * Retrieve customer details (GET /customers/{id})
         * @param string $id Customer ID
         * @return array Customer details (masked)
         */
        public function actionCustomer($id)
        {
            // Placeholder: Replace with actual customer lookup logic
            // Example: Fetch from database or external API
            $customer = null;
            if ($id === 'cust_001') {
                $customer = [
                    'id' => 'cust_001',
                    'name' => 'Alice Example',
                    'email' => 'alice@example.com',
                    'masked_card' => '**** **** **** 1234',
                    'created_at' => '2026-03-01T12:00:00Z',
                    // Add more fields as needed
                ];
            } elseif ($id === 'cust_002') {
                $customer = [
                    'id' => 'cust_002',
                    'name' => 'Bob Example',
                    'email' => 'bob@example.com',
                    'masked_card' => '**** **** **** 5678',
                    'created_at' => '2026-03-02T15:30:00Z',
                    // Add more fields as needed
                ];
            }
            if (!$customer) {
                return ['error' => 'Customer not found'];
            }
            // Mask sensitive fields if needed
            return $this->maskSensitiveArray($customer);
        }
/**
         * Update customer data (PATCH /customers/{id})
         * @param string $id Customer ID
         * @return array Updated customer details (masked)
         */
        public function actionUpdateCustomer($id)
        {
            $request = Yii::$app->request;
            $data = $request->getBodyParams();
            $data = $this->sanitizeArray($data);
            // Placeholder: Replace with actual update logic (DB/API)
            // Simulate update for demo customers
            if ($id === 'cust_001' || $id === 'cust_002') {
                $customer = [
                    'id' => $id,
                    'name' => $data['name'] ?? (($id === 'cust_001') ? 'Alice Example' : 'Bob Example'),
                    'email' => $data['email'] ?? (($id === 'cust_001') ? 'alice@example.com' : 'bob@example.com'),
                    'masked_card' => $data['masked_card'] ?? (($id === 'cust_001') ? '**** **** **** 1234' : '**** **** **** 5678'),
                    'created_at' => (($id === 'cust_001') ? '2026-03-01T12:00:00Z' : '2026-03-02T15:30:00Z'),
                    // Add more fields as needed
                ];
                // Mask sensitive fields if needed
                return $this->maskSensitiveArray($customer);
            }
            return ['error' => 'Customer not found'];
        }
            /**
     * Delete customer (DELETE /customers/{id})
     * @param string $id Customer ID
     * @return array Deletion result (masked)
     */
    public function actionDeleteCustomer($id)
    {
        // Placeholder: Replace with actual delete logic (DB/API)
        // Simulate delete for demo customers
        if ($id === 'cust_001' || $id === 'cust_002') {
            $result = [
                'id' => $id,
                'status' => 'deleted',
                'message' => 'Customer deleted successfully',
            ];
            return $this->maskSensitiveArray($result);
        }
        return ['error' => 'Customer not found'];
    }
        /**
         * Retrieve customers (GET /customers)
         */
        public function actionCustomers()
        {
            // Placeholder: Replace with actual customer retrieval logic
            // Example: Fetch from database or external API
            $customers = [
                [
                    'id' => 'cust_001',
                    'name' => 'Alice Example',
                    'email' => 'alice@example.com',
                    'masked_card' => '**** **** **** 1234',
                    'created_at' => '2026-03-01T12:00:00Z',
                ],
                [
                    'id' => 'cust_002',
                    'name' => 'Bob Example',
                    'email' => 'bob@example.com',
                    'masked_card' => '**** **** **** 5678',
                    'created_at' => '2026-03-02T15:30:00Z',
                ],
            ];
            // Mask sensitive fields if needed
            return $this->maskSensitiveArray($customers);
        }
    /**
     * Sanitize input array (strip tags from strings)
     */
    private function sanitizeArray($arr) {
        foreach ($arr as $k => $v) {
            $arr[$k] = is_string($v) ? strip_tags($v) : $v;
        }
        return $arr;
    }

    /**
     * Mask sensitive data in array (for logs/responses)
     */
    private function maskSensitiveArray($data) {
        $sensitiveKeys = ['password', 'client_secret', 'access_token', 'refresh_token', 'authorization', 'authorization_header', 'secret_key', 'Ds_Signature', 'Ds_MerchantParameters', 'api_key'];
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $data[$key] = $this->maskSensitiveArray($value);
            } elseif (in_array($key, $sensitiveKeys)) {
                $data[$key] = '***MASKED***';
            }
        }
        return $data;
    }

    /**
     * Sanitize output array (strip tags from strings)
     */
    private function sanitizeOutput($arr) {
        if (is_array($arr)) {
            return array_map(function($v) { return is_string($v) ? strip_tags($v) : $v; }, $arr);
        }
        return $arr;
    }

    /**
     * Handle webhook endpoint logic (deduplicated)
     */
    private function handleWebhook($params, $type) {
        $params = $this->sanitizeArray($params);
        $securityError = $this->verifyWebhookSecurity($params);
        if ($securityError) {
            return $this->maskSensitiveArray($securityError);
        }
        if (!empty($params['payId']) && empty($params['Ds_MerchantParameters'])) {
            $payId = filter_var($params['payId'], FILTER_SANITIZE_STRING);
            $details = \PortalPeople\paygate\models\Paygate::retrievePaymentDetails($payId);
            $details = $this->sanitizeOutput($details);
            $details = $this->maskSensitiveArray($details);
            return ['status' => 'ok', 'type' => $type, 'details' => $details];
        }
        $merchantParams = isset($params['Ds_MerchantParameters']) ? strip_tags($params['Ds_MerchantParameters']) : '';
        $result = \PortalPeople\paygate\models\Paygate::processResponse($merchantParams);
        $result = $this->sanitizeOutput($result);
        $result = $this->maskSensitiveArray($result);
        return ['status' => 'ok', 'type' => $type, 'result' => $result];
    }
    public function actionPayment()
    {
        $request = Yii::$app->request;
        $response = Payment::paygatepay($request->get());
        if (is_array($response)) {
            return $this->maskSensitiveArray($response);
        }
        return $response;
    }

    public function actionRequest()
    {
        $request = Yii::$app->request;
        $response = Payment::paygatecreate($request->get());
        if (is_array($response)) {
            return $this->maskSensitiveArray($response);
        }
        return $response;
    }

    // Webhook endpoints
    private function verifyWebhookSecurity($params)
    {
        // Enforce HTTPS (TLS 1.2+)
        // This check ensures webhook requests are made over HTTPS.
        // For full TLS version enforcement, configure your web server (nginx/apache) to only allow TLS 1.2+.
        if (!Yii::$app->request->isSecureConnection) {
            return ['error' => 'Webhook must use HTTPS'];
        }
        // IP whitelist (optional)
        $trustedIps = Yii::$app->params['paygate']['whitelist'] ?? [];
        $remoteIp = Yii::$app->request->userIP;
        // Bypass if whitelist contains '*'
        if (!empty($trustedIps) && !in_array('*', $trustedIps) && !in_array($remoteIp, $trustedIps)) {
            return ['error' => 'IP not allowed'];
        }
        // HMAC signature verification (strict, env var only)
        $merchantParams = $params['Ds_MerchantParameters'] ?? '';
        $signature = $params['Ds_Signature'] ?? '';
        $secret = \PortalPeople\paygate\models\Paygate::getSecretKey();
        if (!$merchantParams || !$signature || !$secret) {
            \Yii::error('Webhook missing merchant parameters, signature, or secret key', __METHOD__);
            return ['error' => 'Missing required webhook fields'];
        }
        // Compute HMAC-SHA-256 signature (base64)
        $computed = base64_encode(hash_hmac('sha256', $merchantParams, $secret, true));
        if (!hash_equals($computed, $signature)) {
            \Yii::error('Webhook HMAC signature verification failed', [
                'expected' => $computed,
                'received' => $signature,
                'params' => $params
            ]);
            return ['error' => 'Invalid webhook signature'];
        }
        $valid = \PortalPeople\paygate\models\Paygate::verifySignature($merchantParams, $signature, $secret);
        if (!$valid) {
            return ['error' => 'Invalid HMAC signature'];
        }
        return null;
    }

    public function actionReturn()
    {
        $request = Yii::$app->request;
        $params = $request->getBodyParams();
        return $this->handleWebhook($params, 'return');
    }

    public function actionCancel()
    {
        $request = Yii::$app->request;
        $params = $request->getBodyParams();
        return $this->handleWebhook($params, 'cancel');
    }

    public function actionWebhook()
    {
        $request = Yii::$app->request;
        $params = $request->getBodyParams();
        return $this->handleWebhook($params, 'webhook');
    }

    public function actionAppRedirect()
    {
        $request = Yii::$app->request;
        $params = $request->getBodyParams();
        return $this->handleWebhook($params, 'appRedirect');
    }
}