Split payment POC: Commerce module AI prompt

Use this page to copy the full prompt that generates the Client_SplitPayment in-process module: REST, session handling, Checkout, and Admin display for the split payment proof of concept. Operator workflow stays in App Builder.

Como usar este prompt

Copie tudo de PROMPT START para End of prompt no Cursor (com Claude) ou diretamente no Claude. Run it from the root of your Commerce project or a directory where the AI can create files.

Customize before you run

  • Replace Client with your real vendor name.
  • Change SplitPayment if you want a different module name.
  • If the site uses a custom theme, layout XML and RequireJS paths may need changes.
  • If your Cash on delivery method uses a different code than cashondelivery, update payment-method-helper.js.

O prompt

INÍCIO DA SOLICITAÇÃO

You are generating a complete, production-ready Adobe Commerce 2.4.5+ in-process module for a split payment feature. This module is the thin PHP adapter that exposes the right REST surface and attaches the right data at the right moments in the Commerce lifecycle. All operator workflow logic lives in Adobe App Builder (not in this module).

Module identity:

  • Vendor: Client
  • Module: SplitPayment
  • Full name: Client_SplitPayment
  • Namespace: Client\SplitPayment
  • Location: app/code/Client/SplitPayment/
  • Dependencies: Magento_Checkout, Magento_CustomerBalance, Magento_Sales, Magento_Quote, Magento_WebApi, Magento_AdobeCommerceEventsClient

Gere cada arquivo listado na estrutura de arquivo abaixo. Não omita nenhum arquivo. Use declare(strict_types=1) em todos os arquivos PHP.

Estrutura de arquivo a ser gerada

app/code/Client/SplitPayment/
├── registration.php
├── composer.json
├── Api/
│   ├── Data/SplitPaymentInterface.php
│   └── SplitPaymentManagementInterface.php
├── Block/
│   └── Order/SplitPaymentInfo.php
├── Controller/
│   └── Checkout/StoreCreditBalance.php
├── etc/
│   ├── acl.xml
│   ├── config.xml
│   ├── db_schema.xml
│   ├── db_schema_whitelist.json
│   ├── di.xml
│   ├── events.xml
│   ├── extension_attributes.xml
│   ├── io_events.xml
│   ├── module.xml
│   ├── webapi.xml
│   └── frontend/
│       └── routes.xml
├── Model/
│   ├── SplitPaymentData.php
│   ├── SplitPaymentManagement.php
│   ├── Service/
│   │   └── SplitInvoiceService.php
│   └── Session/
│       └── SplitPaymentSession.php
├── Observer/
│   ├── AutoInvoiceStoreCreditOnOrderPlace.php
│   └── CopySplitPaymentToOrder.php
├── Plugin/
│   ├── CheckoutPlugin.php
│   ├── OrderRepositoryPlugin.php
│   ├── PlaceOrderPlugin.php
│   ├── Adminhtml/
│   │   └── OrderPaymentPlugin.php
│   ├── Checkout/
│   │   └── LayoutProcessorPlugin.php
│   ├── CustomerBalance/
│   │   └── CapCustomerBalanceCollectPlugin.php
│   ├── Payment/
│   │   ├── Block/
│   │   │   └── AdminSplitPaymentTitlePlugin.php
│   │   └── Checks/
│   │       └── SplitPaymentZeroTotalPlugin.php
│   ├── Quote/
│   │   └── FixSplitPaymentGrandTotalPlugin.php
│   └── Sales/
│       └── FixInvoiceCustomerBalanceAfterTotalsPlugin.php
├── Setup/
│   └── Patch/
│       └── Data/
│           └── AddSplitPaymentOrderAttribute.php
└── view/
    └── frontend/
        ├── requirejs-config.js
        ├── layout/
        │   └── sales_order_view.xml
        ├── templates/
        │   └── order/
        │       └── split-payment-info.phtml
        └── web/
            ├── js/
            │   ├── model/
            │   │   └── payment-method-helper.js
            │   └── view/
            │       └── payment/
            │           ├── cashondelivery-method.js
            │           └── split-payment.js
            └── template/
                └── payment/
                    ├── cashondelivery.html
                    └── split-payment.html

Especificações comportamentais

​1. Esquema de Banco de Dados (etc/db_schema.xml)

Adicionar estas colunas a sales_order (recurso: sales):

Coluna
Tipo
Anulável
Comentário
split_store_credit_amount
decimal(12,4)
sim
Parte de crédito da loja
split_cash_amount
decimal(12,4)
sim
Parte do pagamento à vista devida
split_cash_status
varchar(32)
sim
pending / received / declined
split_sc_invoice_id
int unsigned
sim
ID da entidade da fatura de crédito de armazenamento
split_cash_invoice_id
int unsigned
sim
ID da entidade da fatura de pagamento à vista

Gere também o db_schema_whitelist.json para essas colunas.

​2. Atributos de Extensão (etc/extension_attributes.xml)

Adicionar split_store_credit_amount (flutuante), split_cash_amount (flutuante), split_cash_status (cadeia de caracteres) a:

  • Magento\Quote\Api\Data\CartInterface
  • Magento\Sales\Api\Data\OrderInterface
  • Magento\Sales\Api\Data\OrderPaymentInterface

​3. Pontos de Extremidade REST (etc/webapi.xml)

POST /V1/split-payment/set              → anonymous (session-scoped)
POST /V1/split-payment/orders/:orderId/cash-received  → Magento_Sales::actions
POST /V1/split-payment/orders/:orderId/cash-decline   → Magento_Sales::cancel

Todos os três mapeiam para Client\SplitPayment\Api\SplitPaymentManagementInterface.

​4. Eventos de E/S (etc/io_events.xml)

Inscrever-se em observer.sales_order_place_before e incluir campos:
entity_id, quote_id, increment_id, subtotal, split_store_credit_amount, split_cash_amount, split_cash_status

​5. SplitPaymentSession — Wrapper de Sessões

Armazena valores divididos declarados na sessão de finalização. Deve expor:

  • setAmounts(float $storeCredit, float $cash): void
  • getAmounts(): array — retorna ['store_credit' => float, 'cash' => float]
  • clear(): void
  • beginBalanceApply(): void — define um sinalizador que suprime o plug-in de correção de total geral durante a aplicação de crédito de armazenamento
  • endBalanceApply(): void
  • isBalanceApplyInProgress(): bool

​6. SplitPaymentManagement — Controlador REST

setSplitPayment(float $storeCreditAmount, float $cashAmount, ?string $cartId = null): bool

  • Valida se o carrinho pertence à sessão atual (comparando IDs de cotações numéricas e mascaradas)
  • Armazena valores em SplitPaymentSession
  • Retorna true em caso de sucesso; lança LocalizedException com mensagem genérica em caso de falha

markCashReceived(int $orderId): bool

  • Carrega a ordem por entity_id
  • Valida split_cash_status === 'pending'
  • Define o status para received, estado para processing
  • Adiciona um comentário de histórico: "Cash payment of $X.XX received."
  • Chamadas SplitInvoiceService::createCashPortionInvoice($orderId)
  • Adiciona um comentário com a ID de incremento da fatura à vista
  • Chama createShipmentIfPossible($orderId) — cria uma remessa usando ShipOrder::execute() se houver itens entregáveis
  • Retorna true; lança LocalizedException genérico em qualquer erro

markCashDeclined(int $orderId): bool

  • Carrega a ordem
  • Valida split_cash_status === 'pending'
  • Valida $order->canCancel()
  • Define o status para declined, adiciona o comentário: "Cash payment declined (simulated fraud check)."
  • Salvar pedido
  • Chamadas OrderManagement::cancel($orderId)
  • Retorna true; lança LocalizedException genérico em caso de falha

​7. PlaceOrderPlugin — aroundPlaceOrder em QuoteManagement

Este é o plug-in mais importante. Executa aroundPlaceOrder:

  1. Carregar a cotação; se não estiver ativo, chamar $proceed() imediatamente
  2. Ler SplitPaymentSession::getAmounts()
  3. Se customer_balance_amount_used > 0 estiver na citação, chamar BalanceManagementInterface::remove($cartId) (identificador LocalizedException — registrar e relançar como mensagem genérica)
  4. Chamada recollectQuoteForBalanceOperation() — carrega aspas, chama collectTotals(), salva (em beginBalanceApply() / endBalanceApply() para suprimir o plug-in de correção de total geral)
  5. Se $storeCredit > 0, chamar BalanceManagementInterface::apply($cartId, $storeCredit) (falha do identificador)
  6. Recarregar cotação; definir atributos de extensão: split_store_credit_amount, split_cash_amount, split_cash_status = 'pending'
  7. Salvar payment->additional_information['client_split_payment'] como ['store_credit' => x, 'cash' => y]
  8. Salvar cotação
  9. Chamar $proceed(), capturar ID da ordem
  10. Ligar para SplitPaymentSession::clear()
  11. ID da ordem de devolução

​8. CopySplitPaymentToOrder — Observador em sales_model_service_quote_submit_before

Lê valores divididos de sessão → pagamento additional_information → atributos de extensão de cotação (nessa ordem de prioridade). Grava split_store_credit_amount, split_cash_amount, split_cash_status = 'pending' no objeto de ordem.

​9. AutoInvoiceStoreCreditOnOrderPlace — Observador em sales_order_place_after

Após o posicionamento do pedido, se o pedido tiver um valor de crédito de armazenamento (split_store_credit_amount > 0), chame SplitInvoiceService::createStoreCreditPortionInvoice($orderId) para faturar imediatamente a parte de crédito de armazenamento.

10. SplitInvoiceService

createStoreCreditPortionInvoice(int $orderId): ?int
Cria uma fatura somente para a parte de crédito da loja. Define customer_balance_amount na fatura para o valor de crédito da loja. Registra e salva a fatura. Retorna a ID de entidade da fatura ou nulo na falha (log; não relançar).

createCashPortionInvoice(int $orderId): ?int
Cria uma fatura para a porção de caixa (os itens/valores restantes da ordem não cobertos pela fatura SC). Registra e salva. Retorna a ID da entidade ou nulo em caso de falha.

​11. CheckoutPlugin — em PaymentInformationManagement

Plug-in em Magento\Checkout\Model\PaymentInformationManagement::savePaymentInformationAndPlaceOrder. Antes de continuar:

  • Carregar a cotação da sessão de finalização
  • Obter o limite configurado: Magento\Framework\App\Config\ScopeConfigInterface::getValue('split_payment/general/threshold'), padrão 100
  • Se $quote->getGrandTotal() > $threshold, lançar LocalizedException('Payment could not be processed. Please try again or contact support.')

​12. CapCustomerBalanceCollectPlugin — em Customerbalance total

Depois que a coleta de saldo total do cliente nativo for executada, limite de customer_balance_amount_used a SplitPaymentSession::getAmounts()['store_credit']. Isso evita que o Commerce aplique em excesso o saldo completo do cliente quando ele declarar uma parte menor de crédito da loja.

​13. FixSplitPaymentGrandTotalPlugin — em Quote\Address\Total\Grand

Após a coleta do total geral: se existir uma sessão de pagamento parcelado e isBalanceApplyInProgress() for falso, defina o total geral da cotação para o valor em dinheiro da sessão. Isso faz com que a interface do usuário de check-out mostre apenas o que está vencido em dinheiro.

​14. FixInvoiceCustomerBalanceAfterTotalsPlugin — em Sales\Model\Order\Invoice

Depois que os totais da fatura forem coletados, se a ordem associada da fatura tiver um split_sc_invoice_id, corrija o customer_balance_amount na fatura para evitar a aplicação dupla de crédito de loja.

​15. SplitPaymentZeroTotalPlugin — em Payment\Model\Checks\ZeroTotal

Permitir pagamento de CQO quando SplitPaymentSession::getAmounts()['cash'] > 0, mesmo que o total geral da cotação seja $0 (porque o crédito da loja cobre a ordem inteira).

​16. LayoutProcessorPlugin — em Checkout\Block\Checkout\LayoutProcessor

Depois que o layout for processado:

  • Insira o componente Client_SplitPayment/js/view/payment/split-payment nos filhos additional do componente do método de pagamento cashondelivery em components.checkout.children.steps.children.billing-step.children.payment.children.renders.children.offline-payments.children.cashondelivery.children.additional
  • Remover o componente da interface de usuário de crédito de loja nativa (customerBalance, useStoreCredit) da etapa de pagamento — o componente de pagamento dividido possui a exibição/aplicativo de crédito de loja

​17. OrderRepositoryPlugin — em OrderRepositoryInterface

Depois de get() e getList(), hidratar os atributos de extensão do pedido das sales_order colunas simples (split_store_credit_amount, split_cash_amount, split_cash_status).

​18. AdminSplitPaymentTitlePlugin — em Payment\Block\Info

Depois de getTitle() retornar, se o método de pagamento for cashondelivery e a ordem tiver um pagamento dividido, anexe " (Split: Cash $X.XX + Store Credit $Y.YY)" ao título.

​19. OrderPaymentPlugin — em Sales\Block\Adminhtml\Order\Payment

Em _beforeToHtml ou via afterToHtml, anexe os detalhes do pagamento dividido (valor do pagamento à vista, valor do crédito da loja, status) ao bloco de pagamento HTML na exibição de ordem do Administrador do Commerce.

​20. Controlador de Saldo de Crédito da Loja

Controller/Checkout/StoreCreditBalance.php — rota: GET /splitpayment/checkout/storecreditbalance

Retorna JSON: {"balance": float, "logged_in": bool}. Se o cliente não estiver conectado, retorna {"balance": 0, "logged_in": false}. Saldo de leitura de Magento\CustomerBalance\Model\Balance.

Registrar a rota de front-end em etc/frontend/routes.xml com frontName="splitpayment".

​21. Componente KnockoutJS de Check-out — split-payment.js

Um uiComponent que:

  • Detecta quando o método de pagamento cashondelivery é selecionado (quote.paymentMethod.subscribe)
  • Carrega o saldo de crédito da loja do cliente via GET /splitpayment/checkout/storecreditbalance
  • Preenche previamente o campo de valor em dinheiro com o total completo do pedido (calculado a partir de total_segments excluindo grand_total e customerbalance — nunca usa grand_total diretamente, pois pode ser definido para o restante em dinheiro)
  • À medida que o cliente altera a entrada do valor de caixa: calcula o crédito de armazenamento necessário = total do pedido - caixa; valida; lança em POST /V1/split-payment/set
  • Mostra mensagens de validação para: dinheiro > total do pedido, crédito de armazenamento insuficiente, não conectado
  • Mostra uma mensagem de sucesso quando uma divisão válida é inserida: "The remaining $X.XX will automatically be applied from your store credit."
  • Redefine quando outro método de pagamento é selecionado (lança {storeCreditAmount: 0, cashAmount: 0} para limpar a sessão)

22. cashondelivery-method.js

Estende Magento_OfflinePayments/js/view/payment/offline-payments. Usa payment-method-helper.js para detectar o código do método de pagamento à vista. Registra o componente split-payment em sua região additional.

23. payment-method-helper.js

Utilitário retornando getCashMethodCode() — verifica window.checkoutConfig.paymentMethods para cashondelivery; retorna para checkmo se necessário.

​24. cashondelivery.html Modelo

Modelo CQO padrão, mas inclui a região <!-- ko foreach: getRegion('additional') --> para que o componente filho do pagamento dividido possa ser renderizado.

​25. split-payment.html Modelo

Modelo KnockoutJS para os campos de pagamento dividido:

  • Exibição do saldo de crédito da loja disponível
  • Entrada de valor de caixa (número, etapa 0,01)
  • Exibição da parte de crédito da loja (somente leitura)
  • Aplicar automaticamente a mensagem de crédito da loja (exibida quando a divisão é válida e o crédito da loja > 0)
  • Mensagem de erro de validação

26. requirejs-config.js

Mapas:

  • Client_SplitPayment/js/view/payment/split-payment → o componente
  • Client_SplitPayment/js/view/payment/cashondelivery-method → a substituição do COD
  • Client_SplitPayment/js/model/payment-method-helper → o auxiliar

27. etc/config.xml

Valores padrão de configuração do sistema:

<split_payment>
  <general>
    <threshold>100</threshold>
    <enabled>1</enabled>
  </general>
</split_payment>

Notas de implementação críticas

O aplicativo de crédito de armazenamento deve usar BalanceManagementInterface, não a manipulação de modelo direta. BalanceManagementInterface::apply() manipula atentamente a sessão, a validação e o recálculo do carrinho.

PlaceOrderPlugindeve usar aroundPlaceOrder (não beforePlaceOrder). O crédito da loja deve ser aplicado enquanto o carrinho ainda está ativo, e isso deve ser garantido antes que $proceed() seja chamado.

O padrão de sinalizador de sessão para beginBalanceApply / endBalanceApply é crítico. Sem ele, FixSplitPaymentGrandTotalPlugin é executado durante collectTotals() dentro da operação de saldo e define o total geral como o restante do caixa, fazendo com que BalanceManagementInterface::apply() falhe ou limite o crédito.

Nunca exponha detalhes de erros internos ao cliente. Todos os blocos catch que superam para respostas REST devem lançar LocalizedException('Payment could not be processed. Please try again or contact support.').

entity_idis the numeric database ID. REST calls from App Builder always use entity_id, not increment_id.

SplitInvoiceServiceshould catch and log errors rather than propagate them. Invoice creation failure should not cancel an already-placed order — log the failure and let the Admin handle it manually.

Após gerar os arquivos

Run these commands in the Commerce project root:

bin/magento module:enable Client_SplitPayment
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush

Fim da solicitação

recommendation-more-help
commerce-learn-help-home