klebervmv/bs2pay
Composer 安装命令:
composer require klebervmv/bs2pay
包简介
SDK PHP para BS2 Pay ADIQ E-commerce - Gateway de Pagamento
README 文档
README
SDK PHP completo e pronto para produção da adquirente BS2 Pay ADIQ, construído sobre a biblioteca HTTP easyCurl.
- ✅ Compatível com PHP 7.1 → 8.4
- ✅ OAuth2 com cache e renovação automática de token
- ✅ Sem retry automático — evita risco de cobrança duplicada por timeout
- ✅ Suporte completo a 3DS 2.0 integrado
- ✅ Vault, Zero Auth, recorrência (incluindo nridElo da Elo) e marketplace
- ✅ Webhooks com validação de assinatura HMAC SHA-256 constant-time
- ✅ Logging estruturado em JSON com mascaramento automático de PAN, CVV e tokens
- ✅ Mapeamento dos códigos de retorno de Visa, Mastercard, Elo, Amex e MAC
Instalação
composer require adiq/sdk-php
Dependências:
php≥ 7.1, com extensõesjsonecurlklebervmv/easycurl^1.0
Configuração rápida
require __DIR__ . '/vendor/autoload.php'; use Adiq\AdiqPaymentSDK; $sdk = new AdiqPaymentSDK( 'seu-client-id', 'seu-client-secret', 'sandbox', // sandbox | homologation | production [ 'timeout' => 30000, // ms 'verifySsl' => true, 'logSensitiveData' => false, ] );
Ambientes
| Ambiente | URL |
|---|---|
| sandbox | https://ecommerce-sandbox.adiq.io |
| homologation | https://ecommerce-hml.adiq.io |
| production | https://ecommerce.adiq.io |
Uso
1. Tokenizar cartão (uso único, ~10 min)
$token = $sdk->tokens->create(['cardNumber' => '5155901222250004']); $numberToken = $token->getNumberToken();
2. Criar pagamento (autoriza + captura)
$payment = $sdk->payments->create([ 'payment' => [ 'transactionType' => 'credit', 'amount' => 1000, // R$ 10,00 em centavos 'currencyCode' => 'brl', 'productType' => 'avista', 'installments' => 1, 'captureType' => 'ac', // 'ac' = auth+capture, 'pa' = pre-auth 'recurrent' => false, ], 'cardInfo' => [ 'numberToken' => $numberToken, 'cardholderName' => 'JOSE SILVA', 'securityCode' => '123', 'brand' => 'mastercard', 'expirationMonth' => '12', 'expirationYear' => '30', ], 'customer' => [ 'firstName' => 'Jose', 'lastName' => 'Silva', 'email' => 'jose@example.com', 'documentType' => 'cpf', 'documentNumber' => '51115672088', // ... ], 'sellerInfo' => [ 'orderNumber' => 'ORDER-001', 'softDescriptor' => 'LOJA*TEST', ], ]); if ($payment->isApproved()) { echo "Aprovado: " . $payment->getPaymentId(); } else { echo "Recusado: " . $payment->getDescribedReason(); }
3. Capturar pagamento autorizado (late capture)
$result = $sdk->payments->capture('PAYMENT_ID', 1000); // ou null para valor total
4. Cancelar / refund
$result = $sdk->payments->cancel('PAYMENT_ID'); // total $result = $sdk->payments->cancel('PAYMENT_ID', 500); // parcial
5. Consultar pagamento
$payment = $sdk->payments->get('PAYMENT_ID', 'v2'); $payment = $sdk->payments->getByOrderNumber('ORDER-001', '20260513');
6. Vault (one-click / recorrência)
$vault = $sdk->vault->store([ 'numberToken' => $numberToken, 'cardholderName' => 'JOSE SILVA', 'brand' => 'mastercard', 'expirationMonth' => '12', 'expirationYear' => '30', 'verifyCard' => true, // Zero Auth antes de salvar ]); $vaultId = $vault->getVaultId(); // Usar vaultId em vez de numberToken na próxima cobrança $payment = $sdk->payments->create([ 'payment' => [...], 'cardInfo' => ['vaultId' => $vaultId, ...], // ... ]);
7. Zero Auth (validar cartão sem cobrar)
$result = $sdk->zeroAuth->verify(['vaultId' => $vaultId]);
8. Consulta BIN
$bin = $sdk->bin->search('515590'); // $bin->getBody() retorna { bank, brand, country, type, ... }
9. 3DS 2.0 integrado
$code3DS = $sdk->threeDs->generateCode3DS(); $deviceInfo = $sdk->threeDs->buildDeviceInfo([ 'colorDepth' => 24, 'javaEnabled' => false, 'language' => 'pt-BR', 'screenHeight' => 1080, 'screenWidth' => 1920, 'timeZone' => 180, ]); $threeDs = $sdk->threeDs->buildThreeDsBlock($code3DS, '01'); $payment = $sdk->payments->create([ 'payment' => [...], 'cardInfo' => [...], 'customer' => [...], 'sellerInfo' => [...], 'deviceInfo' => $deviceInfo, 'threeDs' => $threeDs, 'code3DS' => $code3DS, ]); $result = $sdk->threeDs->interpretResponse($payment->getBody()); if ($result['challenge']) { // Redirecionar para $result['acsUrl'] com $result['pareq'] }
10. Webhook
// CLI: registrar URL de callback (rodar uma vez) $sdk->webhook->register('https://seudominio.com/adiq/callback', [ 'X-Custom-Token' => 'segredo-compartilhado', ]); // Handler HTTP use Adiq\Webhook\WebhookValidator; $rawBody = file_get_contents('php://input'); $signature = $_SERVER['HTTP_X_ADIQ_SIGNATURE'] ?? ''; if (!WebhookValidator::verifySignature($rawBody, $signature, $secret)) { http_response_code(401); exit; } $payload = WebhookValidator::parsePayload($rawBody); // ... processar
Tratamento de erros
Todas as exceptions herdam de Adiq\Exceptions\AdiqException:
| Exception | Quando ocorre |
|---|---|
ValidationException |
Validação local OU HTTP 400 |
AuthException |
HTTP 401 / 403, falha de OAuth |
NotFoundException |
HTTP 404 |
RateLimitException |
HTTP 429 (com getRetryAfter()) |
PaymentException |
Recusa de bandeira (com getReturnCode(), MAC, etc) |
NetworkException |
Timeout, DNS, conexão recusada, 5xx persistente |
try { $payment = $sdk->payments->create([...]); } catch (\Adiq\Exceptions\ValidationException $e) { foreach ($e->getErrors() as $field => $msg) { /* ... */ } } catch (\Adiq\Exceptions\PaymentException $e) { $rc = $e->getReturnCode(); $mac = $e->getMerchantAdviceCode(); } catch (\Adiq\Exceptions\AdiqException $e) { // Genérico }
Política de retry — leia antes
O SDK não faz retry automático. Essa é uma decisão deliberada de segurança.
Em pagamentos, um POST /v1/payments que falha com timeout ou 5xx pode ter sido processado com sucesso pelo servidor — apenas a resposta se perdeu. Retentar automaticamente nesse caso pode debitar o cartão do cliente duas vezes.
Como lidar com falhas incertas
-
Use
orderNumberúnico e determinístico por pedido (emsellerInfo.orderNumber). -
Em caso de
NetworkExceptionempayments->create(), não retente cegamente. Antes, consulte:try { $payment = $sdk->payments->create([ 'payment' => [...], 'cardInfo' => [...], 'sellerInfo' => ['orderNumber' => 'ORDER-001', ...], ]); } catch (\Adiq\Exceptions\NetworkException $e) { // Verifica se a transação chegou a ser registrada na ADIQ try { $existing = $sdk->payments->getByOrderNumber('ORDER-001', date('Ymd')); if ($existing->isApproved()) { // Já aprovada — não reenvie, apenas use o paymentId retornado } else { // Não existe / falhou — seguro reenviar } } catch (\Adiq\Exceptions\NotFoundException $e) { // Não chegou ao servidor; seguro reenviar } }
Quando retentar é seguro (decisão do caller)
| Operação | Seguro retentar? |
|---|---|
| GET (consultas) | ✅ Sempre — idempotente |
| DELETE vault | ✅ — idempotente |
POST /v1/tokens/cards |
⚠️ Baixo risco — não cobra |
POST /v1/payments |
❌ Apenas após verificar via getByOrderNumber |
PUT capture/cancel |
❌ "Já capturado" pode ser falso-OK |
RateLimitException (HTTP 429) expõe getRetryAfter() — você pode aguardar e reenviar manualmente.
Logging
Por padrão, o SDK loga em JSON em STDERR com mascaramento automático de campos sensíveis (cardNumber, securityCode, numberToken, clientSecret, authorization, etc).
Para usar logger customizado:
use Adiq\Utils\Logger; $logger = new Logger('debug', false, __DIR__ . '/logs/adiq.log'); $sdk = new AdiqPaymentSDK($id, $secret, 'sandbox', ['logger' => $logger]);
Estrutura
src/
├── AdiqPaymentSDK.php # Entry-point
├── Config/Config.php
├── Auth/{AuthManager, CredentialsInterface}.php
├── Client/{HttpClient, Request}.php
├── Payment/{PaymentClient, TokenClient, VaultClient, ZeroAuthClient, BinClient}.php
├── ThreeDs/{ThreeDsClient, ThreeDsValidator}.php
├── Webhook/{WebhookManager, WebhookValidator}.php
├── Dto/{ResponseDto, PaymentDto, CardDto, CustomerDto, ThreeDsResponseDto, ErrorResponseDto}.php
├── Exceptions/{AdiqException, ValidationException, AuthException, PaymentException, NetworkException, NotFoundException, RateLimitException}.php
└── Utils/{Validator, Logger, ErrorMapper, Helper}.php
examples/
├── basic-payment.php
├── 3ds-flow.php
├── vault-payment.php
├── recurring.php
└── webhook-setup.php
tests/
├── integration.php # Suite end-to-end contra sandbox/homologation
└── 3ds-challenge.html # Helper visual para simular o challenge 3DS no browser
Executando os testes
# Copie .env.example para .env e preencha CLIENT_ID/CLIENT_SECRET, então:
php tests/integration.php
O tests/3ds-challenge.html é uma página auxiliar — abra direto no browser
(via Laragon ou servidor PHP embutido) para concluir manualmente o passo de
challenge do fluxo 3DS 2.0.
Boas práticas
- Valores em centavos:
amounté sempre inteiro (1000= R$ 10,00). - PCI-DSS: nunca persista PAN ou CVV. Use
tokens->create()no momento da cobrança ouvault->store()para armazenar. - Idempotência: use
sellerInfo.orderNumberúnico por pedido — permite consulta posterior porpayments->getByOrderNumber(). - Webhooks: sempre valide a assinatura HMAC e responda 2xx rapidamente; processe em background.
- 3DS: mesmo após captura imediata (
ac), inclua deviceInfo + threeDs para ter liability shift. - Recorrência Elo: salve
nridEloda primeira transação para reenviar nas subsequentes.
Próximos passos
- /help:
/helpna CLI Claude Code - Feedback: https://github.com/anthropics/claude-code/issues
Licença
MIT — veja LICENSE.
统计信息
- 总下载量: 1
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 0
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-01