一个PHP组件/库。

v0.2.0 2024-09-03 13:26 UTC

This package is auto-updated.

Last update: 2024-09-03 13:33:07 UTC


README

一个XPay (Nexi) 实现。

当前实现的功能

简单支付

扩展类 AbstractSimplePaymentService

  • 实现 SimplePaymentServiceInterface
    • createCancelUrl;
    • createNotificationUrl;
    • createReturnUrl;
final class SimplePaymentService extends AbstractSimplePaymentService implements SimplePaymentServiceInterface
{
    protected function createCancelUrl(string $orderId): string
    {
        ...
    }

    protected function createNotificationUrl(string $orderId): string
    {
        ...
    }

    protected function createReturnUrl(string $orderId): string
    {
        ...
    }
}

扩展类 AbstractRequestInputService

  • 可选地实现你自己的验证规则;
final class RequestInputService extends AbstractRequestInputService implements RequestInputServiceInterface
{
    public const KEY_LANGUAGE = 'lang';
    
    public const KEY_ORDER_ID = 'orderId';
    
    protected function getValidationRule(string $key): string
    {
        return match ($key) {
            self::KEY_ORDER_ID => '/^[a-f0-9]{42}$/',
            default => parent::getValidationRule($key),
        };
    }
    
    protected function validateInput(string $key, string $value): bool
    {
        if ($key === self::KEY_LANGUAGE) {
            return $this->validateLanguageCode($value);
        }

        return parent::validateInput($key, $value);
    }
    
    private function validateLanguageCode(string $value): bool
    {
        if (!in_array($value, ['en', 'it'], true)) {
            throw new UnexpectedValueException('Invalid data.');
        }

        return true;
    }
}

支付请求

<!doctype html>
<html>
    <head>
        ...
    </head>
    <body>
        <form id="payment_form" method="POST" action="<?=$paymentService->getSimplePaymentStartUrl()?>">
            <?php
            foreach (
                $paymentService->createPaymentRequestParameters(
                    $languageCode,
                    $orderId,
                    $orderInformation->total,
                ) as $key => $value
            ) { ?>
                <input type="hidden" name="<?=$key?>" value="<?=$value?>" />
            <?php } ?>
            <button type="submit" class="btn btn-primary">
                <?=$languageCode === 'it' ? 'Acquista ora' : 'Buy now'?>
            </button>
        </form>
        <script>
            document.getElementById('payment_form').submit();
        </script>
    </body>
</html>

返回页面

// Validate order (get info from storage)
...

/**
 * Special situation: "mac" can be missing from the request.
 * Eg. try to pay already paid transaction.
 * In that situation we don't want to have a transaction error, however we also can not trust the request.
 * Simply do not process transaction.
 */
$processTransaction = true;
try {
    // Try to get mac
    $requestInputService->getValidatedString(RequestInput::MAC);
} catch (OutOfBoundsException) {
    $processTransaction = false;
}

if ($processTransaction) {
    // Validate transaction. Uses input data (_GET or _POST).
    $requestInputService->validateInputMac();

    // Store transaction result.
    ...
}

// Redirect back to website.
...

周期性支付

扩展类 AbstractRecurringPaymentService

  • 实现 RecurringPaymentServiceInterface
    • createCancelUrl;
    • createNotificationUrl;
      • createReturnUrl;
final class RecurringPaymentService extends AbstractRecurringPaymentService implements RecurringPaymentServiceInterface
{
    protected function createCancelUrl(string $orderId): string
    {
        ...
    }

    protected function createNotificationUrl(string $orderId): string
    {
        ...
    }

    protected function createReturnUrl(string $orderId): string
    {
        ...
    }
}

扩展类 AbstractRequestInputService

  • 可选地实现你自己的验证规则;
  • 参见“简单支付”中的相同部分

第一次支付:支付请求

<!doctype html>
<html>
    <head>
        ...
    </head>
    <body>
        <form id="payment_form" method="POST" action="<?=$paymentService->getRecurringPaymentInitialUrl()?>">
            <?php
            foreach (
                $paymentService->createInitialPaymentRequestParameters(
                    $languageCode,
                    $numContratto,
                    $orderInformation->total,
                ) as $key => $value
            ) { ?>
                <input type="hidden" name="<?=$key?>" value="<?=$value?>" />
            <?php } ?>
            <button type="submit" class="btn btn-primary">
                <?=$languageCode === 'it' ? 'Acquista ora' : 'Buy now'?>
            </button>
        </form>
        <script>
            document.getElementById('payment_form').submit();
        </script>
    </body>
</html>

第一次支付:返回页面

  • 参见“简单支付”中的相同部分

后续支付

  • 后续支付

  • 使用 SubsequentPaymentService,方法 executeSubsequentPayment(string $numeroContratto, float $orderTotal, string $scadenza): AbstractResponseData

  • 也可以使用 SubsequentPaymentServicegetResponse,在运行 executeSubsequentPayment 后返回 Psr\Http\Message\ResponseInterface,否则返回 null。

  • 检查结果是否为 PositiveResponseDataNegativeResponseData 并相应地行动。

开发

# Lint
composer check:lint && \
# Code style
composer check:phpcs && \
# PHPStan
composer check:phpstan && \
# Phan
composer check:phan && \
# PHPMD
composer check:phpmd && \
# Psalm
composer check:psalm