POC de pagamento dividido: decisões de arquitetura e design
Esta página explica as escolhas arquitetônicas por trás da prova de conceito do pagamento dividido. Leia-o antes de usar os prompts de build desta série para entender como cada componente está estruturado e como adaptar os padrões no seu próprio projeto.
Princípio fundamental
A prova de conceito não é sobre a implementação de pagamento dividido mais elegante. Trata-se de mostrar como começar a mover a lógica do Commerce para o App Builder sem uma regravação big bang.
A regra aplicada em todo o é:
Se algo precisar ser executado de forma síncrona no ciclo de solicitações do Commerce, ou precisar invocar APIs internas da Commerce que não tenham superfície externa limpa, ele permanecerá no PHP. Todo o resto é movido para o App Builder.
O que existe no Commerce (PHP) e por quê
1. Aplicativo de crédito de armazenamento: PlaceOrderPlugin
O crédito de armazenamento é aplicado ao carrinho usando Magento\CustomerBalance\Api\BalanceManagementInterface::apply(). Este método só funciona em um carrinho ativo. O carrinho fica inativo no momento em que o pedido é feito. A App Builder recebe o evento de E/S depois de o pedido ser feito, portanto, não é possível aplicar crédito de loja da App Builder.
A lição: tudo o que precisar mudar o estado do carrinho antes de fazer pedidos deve ser executado no Commerce. Não há solução alternativa.
2. Proteção de limite síncrona: CheckoutPlugin
A verificação de limite de pedido de $100 deve bloquear o cliente na etapa de pagamento, antes que ele selecione Place Order. A resposta deve ser síncrona no ciclo de solicitação do Commerce. O App Builder é orientado por eventos e assíncrono, portanto, não pode retornar um erro imediato nesse momento.
O App Builder also valida o limite (como uma auditoria), mas a experiência do cliente depende da execução da verificação do Commerce primeiro.
3. Pontos de extremidade REST personalizados: webapi.xml e SplitPaymentManagement
Os seguintes endpoints devem:
- Invocar
SplitInvoiceService(faturas que usam o serviço de fatura interna da Commerce) - Chamar
ShipOrder::execute()(serviço de remessa interna da Commerce) - Atualizar o estado e o status do pedido com a máquina de estado do pedido da Commerce
/V1/split-payment/orders/:id/cash-received e /V1/split-payment/orders/:id/cash-decline
Não há camada REST pública limpa para esse comportamento, portanto, o Commerce expõe os endpoints. O App Builder as chama de.
4. Dividir os valores por cotação e por ordem: observadores e plugins
O Commerce precisa dos valores divididos (split_store_credit_amount, split_cash_amount, split_cash_status) no pedido, tanto para as respostas REST lidas pelo App Builder quanto para a exibição de pedido Admin. Os valores são anexados com atributos de extensão e são copiados da citação para a ordem em um observador em sales_model_service_quote_submit_before.
O que está no App Builder e por quê
1. Processamento de pedido orientado por evento: payment-orchestrator
Depois que sales_order_place_before for acionado, a App Builder receberá o evento. Ele revalida o limite (como uma auditoria), registra um comentário de dinheiro pendente no pedido e atualiza o status do pedido. Nada disso requer PHP novo, apenas REST de volta para Commerce.
2. Aceitação de dinheiro: payment-accept
Quando um ERP (ou um operador no painel) confirma que o dinheiro foi recebido, payment-accept chama POST /V1/split-payment/orders/:id/cash-received. A fatura, a remessa e o status do pedido são tratados no Commerce. O acionador é o App Builder.
3. Recusa em dinheiro: payment-decline
payment-decline chama POST /V1/split-payment/orders/:id/cash-decline e o Commerce cancela o pedido. Mesmo padrão que a aceitação de caixa.
4. Painel do operador: demo-dashboard
Um painel do HTML independente veiculado a partir de uma ação da Web do App Builder. Ele busca pedidos que estão aguardando dinheiro do Commerce REST e fornece Accept / Decline ações que chamam as ações do App Builder acima. Commerce Admin is not required.
The threshold: enforced twice on purpose
Customer at checkout
|
v
[Commerce: CheckoutPlugin] <- Synchronous, blocks immediately, user sees error
|
| (if somehow bypassed: direct API call, and so on)
v
[Order placed] -> I/O Event -> [App Builder: payment-orchestrator]
|
v
[evaluateThreshold()] <- Async audit, records failure comment
Commerce owns the user-facing guard; App Builder owns the post-placement audit. That is intentional.
The store credit: why it stays in PHP
What you might think would work (it does not):
Order placed -> I/O Event -> App Builder -> PUT /V1/carts/:id/store-credit
(Fails: cart is inactive after place order)
What actually works:
AroundPlaceOrder plugin
-> BalanceManagementInterface::apply($cartId, $amount) <- cart is still active
-> place order
-> order placed
-> I/O event: App Builder (store credit is already applied)
The store-credit.js file in the orchestrator documents this. It is a no-op stub with comments that explain why it is not used.
Extension attributes: the glue
Split amounts move through the system on extension attributes:
Checkout JavaScript (Knockout)
| POST /V1/split-payment/set
v
SplitPaymentSession (PHP session)
| AroundPlaceOrder reads the session
v
CartInterface extension attributes
| `sales_model_service_quote_submit_before` observer
v
OrderInterface extension attributes -> `sales_order` flat columns
| I/O event payload includes these fields
v
App Builder `payment-orchestrator` reads the split amounts
Modelo de dados
sales_orderflat columns that this module adds
split_store_credit_amountsplit_cash_amountsplit_cash_statuspending, received, or declinedsplit_sc_invoice_idsplit_cash_invoice_idExtension attributes (on CartInterface, OrderInterface, and OrderPaymentInterface)
split_store_credit_amount(flutuante)split_cash_amount(flutuante)split_cash_status(cadeia de caracteres)
Campos de carga útil do evento de E/S
observer.sales_order_place_before está configurado em io_events.xml para incluir o seguinte no evento:
entity_id, quote_id, increment_id, subtotal,
split_store_credit_amount, split_cash_amount, split_cash_status
A App Builder usa entity_id como a ID do pedido e split_store_credit_amount e split_cash_amount para validação de limite.
Os cinco casos de borda que a prova de conceito cobre
1. CapCustomerBalanceCollectPlugin
O coletor total Customer balance nativo do Commerce pode aplicar em excesso (pode ver o saldo disponível completo, não o valor de divisão declarado pela sessão). Este plug-in limita a quantidade ao valor declarado na sessão.
2. FixSplitPaymentGrandTotalPlugin
Depois que o crédito de armazenamento é aplicado, a cotação Grand Total pode cair somente para o valor de caixa. A JavaScript de check-out deve calcular o total do pedido para validação de divisão antes dessa alteração. O plug-in é executado após a coleta de totais e corrige a exibição, enquanto o JavaScript não confia em grand_total sozinho e reconstrói o valor a partir de segmentos de subtotal.
3. FixInvoiceCustomerBalanceAfterTotalsPlugin
Quando os totais da fatura são recuperados, o crédito da loja pode ser aplicado duas vezes. Este plug-in corrige customer_balance_amount nas faturas.
4. SplitPaymentZeroTotalPlugin
Depois que o crédito de armazenamento é aplicado, o carrinho Grand Total pode ser de $0 (ordem de crédito de armazenamento completa). A verificação Zero subtotal checkout da Commerce pode bloquear o COD nesse caso. Este plug-in permite o COD quando o valor do dinheiro da sessão é maior que 0.
5. Lembrança de cotação antes de BalanceManagementInterface::apply()
apply() verifica o valor em relação ao Grand Total atual. Se o total já for apenas a parte de caixa, apply() pode falhar ou atingir. PlaceOrderPlugin suspende temporariamente a correção de total geral enquanto o saldo é aplicado, usando um sinalizador de sessão (beginBalanceApply / endBalanceApply).
Related split payment POC resources
- Criar uma POC de pagamento dividido: ferramentas do App Builder e da IA
- Criar uma POC de pagamento dividido: demonstração completa do App Builder
- POC de pagamento dividido: decisões de arquitetura e design
- POC de pagamento dividido: pré-requisitos e configuração de ambiente
- POC de pagamento dividido: referência de variáveis de ambiente
- Split payment POC: Commerce module AI prompt
- POC de pagamento dividido: prompt do App Builder orchestrator AI
- POC de pagamento dividido: prompt da IA de extensão da interface do usuário do Experience Cloud
- POC de pagamento dividido: guia de teste e verificação
- POC de pagamento dividido: próximas etapas após a prova de conceito
- Split payment POC: tutorial quick reference for authors