분할 결제 POC: App Builder orchestrator AI 프롬프트
분할 결제-오케스트레이터 프로젝트를 생성하는 전체 프롬프트, 즉 결제-오케스트레이터 I/O 이벤트 소비자, 결제-수락 및 결제-거절 웹 액션, 데모-대시보드 및 Commerce REST 클라이언트를 복사할 수 있습니다.
이 프롬프트를 사용하는 방법
PROMPT START에서 PROMPT END까지의 모든 내용을 Cursor(Claude 포함)로 복사하거나 Claude로 직접 복사합니다. split-payment-orchestrator/ 디렉터리(App Builder 프로젝트 루트)에서 프롬프트를 실행합니다.
실행하기 전에
- 분할 결제 POC: 필수 구성 요소 및 환경 설정을 완료합니다.
- 분할 결제 POC: 환경 변수 참조 및
.env파일을 프로젝트에서 준비하십시오.
프롬프트
프롬프트 시작
Adobe Commerce에서 분할 지급을 조정하기 위해 전체 Adobe App Builder 애플리케이션을 생성하고 있습니다. 이 애플리케이션은 Commerce에서 I/O 이벤트를 수신하고 분할 지급 결정을 처리하며 REST를 통해 Commerce으로 다시 호출합니다.
프로젝트: split-payment-orchestrator
런타임: Node.js 18
키 종속성: @adobe/aio-sdk ^6.0.0, got ^11.8.6, oauth-1.0a ^2.2.6
아래 나열된 모든 파일을 생성합니다. 응용 프로그램은 Adobe I/O Runtime(aio app deploy)에서 작동해야 합니다.
생성할 파일 구조
split-payment-orchestrator/
├── package.json
├── app.config.yaml
├── .env.example
└── actions/
├── payment-orchestrator/
│ ├── index.js ← I/O Event entry point
│ ├── commerce-client.js
│ ├── threshold.js
│ ├── cash-payment.js
│ ├── order-update.js
│ └── store-credit.js ← Deprecated stub; kept for reference
├── payment-accept/
│ └── index.js
├── payment-decline/
│ └── index.js
└── demo-dashboard/
└── index.js
package.json
{
"name": "split-payment-orchestrator",
"version": "1.0.0",
"private": true,
"description": "Adobe App Builder action — split payment PoC orchestrator",
"engines": { "node": ">=18" },
"scripts": {
"deploy": "NODE_OPTIONS=--disable-warning=DEP0040 aio app deploy",
"aio": "NODE_OPTIONS=--disable-warning=DEP0040 aio"
},
"dependencies": {
"@adobe/aio-sdk": "^6.0.0",
"@adobe/exc-app": "^1.5.9",
"got": "^11.8.6",
"oauth-1.0a": "^2.2.6"
}
}
app.config.yaml
split_payment_orchestrator 패키지에서 네 가지 작업을 정의합니다.
payment-orchestrator
web: "no"(I/O 이벤트 트리거만 — HTTP를 통해 직접 호출할 수 없음)runtime: nodejs:18require-adobe-auth: true,final: true- 환경 입력:
LOG_LEVEL,COMMERCE_BASE_URL,COMMERCE_CONSUMER_KEY,COMMERCE_CONSUMER_SECRET,COMMERCE_ACCESS_TOKEN,COMMERCE_ACCESS_TOKEN_SECRET,PAYMENT_THRESHOLD
payment-accept
web: "yes"(대시보드 또는 ERP에서 호출할 수 있는 HTTP 웹 작업)runtime: nodejs:18require-adobe-auth: true,final: true- 동일한 Commerce 자격 증명 입력(
PAYMENT_THRESHOLD없음)
payment-decline
web: "yes"runtime: nodejs:18require-adobe-auth: true,final: true- 동일한 Commerce 자격 증명 입력
demo-dashboard
web: "yes"runtime: nodejs:18require-adobe-auth: false← 대시보드는 공개적으로 액세스할 수 있습니다(설정된 경우DEMO_UI_SECRET에 의해서만 보호).- 입력: 모든 Commerce 자격 증명 +
DEMO_UI_SECRET,DEMO_UI_BASE_URL
이벤트 등록(events.registrations 아래):
Split payment — sales order place before:
description: Invokes payment-orchestrator when an order is about to be placed
events_of_interest:
- provider_metadata: dx_commerce_events
event_codes:
- com.adobe.commerce.observer.sales_order_place_before
runtime_action: split_payment_orchestrator/payment-orchestrator
actions/payment-orchestrator/commerce-client.js
Adobe Commerce용 OAuth 1.0a REST 클라이언트를 공유했습니다. 구현:
createCommerceClient(params, logger) — { request, baseUrl } 반환
params에서COMMERCE_BASE_URL,COMMERCE_CONSUMER_KEY,COMMERCE_CONSUMER_SECRET,COMMERCE_ACCESS_TOKEN,COMMERCE_ACCESS_TOKEN_SECRET읽기- 누락된 자격 증명이 있는 경우 throw됩니다.
HMAC-SHA256과(와) 함께oauth-1.0a사용(Node.jscrypto.createHmac)prefixUrl = ${baseUrl}/rest/V1/에서got@11(got@12+이(가) 아님) 사용 - 프로젝트에서 CJS 사용beforeRequest후크를 통해Authorization헤더 추가request(method, path, options)— 경로(/앞에 있는 스트립)를 정규화하고{ statusCode, body }을(를) 반환합니다.throwHttpErrors: false— 4xx/5xx에서 throw되지 않습니다. 항상 상태 코드를 반환합니다.
actions/payment-orchestrator/threshold.js
evaluateThreshold({ orderTotal, storeCreditAmount, cashAmount, params, logger }) — { pass: boolean, reason: string } 반환
논리:
params.PAYMENT_THRESHOLD을(를) 읽고, float로 구문 분석합니다. 누락된 경우 기본적으로100, NaN 또는 ≤ 0입니다.orderTotal > threshold인 경우:{ pass: false, reason: 'PAYMENT_THRESHOLD_EXCEEDED' }반환Math.abs((storeCreditAmount + cashAmount) - orderTotal) > 0.02인 경우:{ pass: false, reason: 'SPLIT_AMOUNT_MISMATCH' }반환- 그렇지 않으면
{ pass: true, reason: '' }반환
actions/payment-orchestrator/cash-payment.js
recordCashPending({ commerce, orderId, cashAmount, logger }) — { ok: boolean, error? } 반환
-
다음으로
POST orders/${orderId}/comments호출:code language-json { "statusHistory": { "comment": "Cash payment of $X.XX pending. Awaiting admin confirmation.", "entity_name": "order", "parent_id": "<orderId>", "is_visible_on_front": true, "is_customer_notified": false, "status": "pending_payment" } } -
2xx에
{ ok: true }을(를) 반환합니다. 그렇지 않으면{ ok: false, error: { code, message } }을(를) 반환합니다. -
try/catch로 래핑, 오류 개체를 반환하고 throw하지 않음
actions/payment-orchestrator/order-update.js
updateOrderAfterOrchestration({ commerce, orderId, success, detail, logger }) — { ok: boolean, error? } 반환
success: 기록 댓글을 게시하는 경우"Split payment orchestration completed. Order awaiting cash confirmation."!success: 게시물"Payment could not be processed. Please try again or contact support."— 고객이 볼 수 있는 댓글에detail을(를) 포함하지 않음. 내부적으로만detail기록{ ok: boolean, error? }을(를) 반환합니다. throw하지 않습니다.
actions/payment-orchestrator/store-credit.js
applyStoreCredit({ commerce, cartId, amount, logger }) — 더 이상 사용되지 않는 no-op 구현
다음을 설명하는 JSDoc @deprecated 알림과 함께 이 파일을 포함합니다.
오케스트레이터는 더 이상 REST를 통해 스토어 크레딧을 적용하지 않습니다. 저장소 크레딧이 Commerce PHP 모듈(
PlaceOrderPlugin)의 체크 아웃 시BalanceManagementInterface::apply()을(를) 사용하여 적용되며, 이 경우 활성 장바구니가 필요합니다. App Builder이 I/O 이벤트를 수신할 때까지 장바구니는 비활성 상태입니다. 이 파일은 참조용으로 보관되거나 스토어 크레딧이 사후 적용되는 사용자 지정 플로우용으로 보관됩니다.
개발자가 패턴을 학습할 수 있도록 작동 중인 구현(다른 모듈과 동일한 모양)을 유지하지만 현재 흐름에서 사용되지 않는 것으로 명확하게 표시합니다.
actions/payment-orchestrator/index.js
입출력 이벤트 진입점 async function main(params) 구현.
페이로드 추출:
Adobe Commerce I/O 이벤트는 페이로드를 여러 가지 모양으로 전달할 수 있습니다. 다음 경로를 순서대로 확인하여 순서 값 개체를 추출합니다.
params.__ow_body(필요한 경우 JSON 문자열 구문 분석) →.event.data.valueparams.data.valueparams.valueparams.body.event.data.valueparams자체로 폴백
값에서 필드 추출:
-
orderId = value.entity_id || value.id -
orderTotal = parseFloat(value.grand_total ?? value.base_grand_total ?? value.subtotal ?? '0') -
value.extension_attributes에서 분할 금액(extension_attributes및extensionAttributes모두 확인):storeCredit = parseFloat(ext.split_store_credit_amount ?? value.split_store_credit_amount ?? '0')cash = parseFloat(ext.split_cash_amount ?? value.split_cash_amount ?? '0')
흐름:
- 값을 추출합니다.
orderId이(가) 없으면 오류를 기록하고{ statusCode: 200, body: { ok: false, message: PUBLIC_ERROR } }을(를) 반환합니다. evaluateThreshold(...)호출 — 실패하면 같은 200 응답을 로그하여 반환합니다.createCommerceClient(params, logger)호출 — 실패하면 200 오류를 반환합니다.storeCredit > 0인 경우 체크아웃 시 해당 저장소 크레딧이 적용되었는지 기록합니다(REST 호출 필요 없음).recordCashPending(...)호출 — 실패하면updateOrderAfterOrchestration(..., success: false)을(를) 호출하고 200 오류를 반환합니다.updateOrderAfterOrchestration(..., success: true)호출{ statusCode: 200, body: { ok: true, message: 'processed' } }반환
중요: 항상 statusCode: 200을(를) 반환합니다. I/O Runtime이 200개가 아닌 응답을 다시 시도하면 중복 주문 처리가 발생합니다. 본문에 오류가 보고됩니다.
PUBLIC_ERROR상수: "Payment could not be processed. Please try again or contact support." — 모든 외부 오류 메시지에 사용됩니다.
actions/payment-accept/index.js
HTTP 웹 작업입니다. POST /V1/split-payment/orders/:orderId/cash-received을(를) 호출합니다.
주문 ID 확인: params.orderId, params.payload.orderId, params.__ow_body(구문 분석된 JSON)을 확인합니다. 누락된 경우 400을 반환합니다.
흐름:
orderId해결, 누락된 경우 400 반환- 상거래 클라이언트 초기화, 실패 시 500 반환
- 빈 JSON 본문으로
POST split-payment/orders/${orderId}/cash-received호출 - 2xx인 경우:
{ statusCode: 200, body: { ok: true, orderId, message: 'accepted' } }반환 - 오류인 경우: 로그하여
{ statusCode: 200, body: { ok: false, message: PUBLIC_ERROR } }반환
actions/payment-decline/index.js
HTTP 웹 작업입니다. payment-accept과(와) 같은 패턴이지만 POST /V1/split-payment/orders/:orderId/cash-decline을(를) 호출합니다.
성공하면 { ok: true, orderId, message: 'declined' }을(를) 반환합니다.
actions/demo-dashboard/index.js
자체 포함된 데모 연산자 대시보드. 보류 중인 현금 주문을 나열하고 수락/거절 작업을 트리거하기 위한 HTML 대시보드를 제공합니다. HTML UI와 JSON API를 모두 제공하는 단일 웹 작업입니다.
보안:
- 선택적
DEMO_UI_SECRET확인: 설정된 경우 모든 요청에?secret=<value>쿼리 매개 변수 또는x-demo-secret헤더가 필요합니다. 누락/잘못된 경우 401을 반환합니다. DEMO_UI_SECRET이(가) 설정되지 않은 경우 경고 기록(대시보드가 보호되지 않음)
라우팅(HTTP 메서드 + 경로/본문 기반):
이 작업은 모든 요청을 수신합니다. 다음에서 의도 결정:
params.__ow_method(GET/POST) 및params.__ow_path또는 작업 매개 변수- HTML 대시보드를 제공하는 작업이 →
GET action=list매개 변수가 있는GET이(가) 보류 중인 주문의 JSON 목록을 반환할 → 있습니다.action=accept및orderId이(가) 있는POST에서payment-accept논리를 →(또는 Commerce REST 호출을 인라인으로)action=decline및orderId을(를) 사용하는POST에서payment-decline논리를 →.
보류 중인 주문 가져오기:
- Commerce REST에서
GET orders?searchCriteria[pageSize]=50호출 extension_attributes.split_cash_status === 'pending'및 순서가 터미널 상태가 아닌 순서로 필터링합니다(complete,closed,canceled,cancelled).- 메모리에서 가장 최근 항목 먼저 정렬
- 최대 20개 반환(
limit매개 변수를 통해 구성 가능)
HTML 대시보드:
대시보드는 Content-Type: text/html(으)로 반환된 HTML 문자열입니다. 이는 다음과 같아야 합니다.
- 보류 중인 현금 주문을 테이블에 나열합니다. 주문 번호(increment_id), entity_id, 고객명, 현금 금액, 상점 대변 금액, 일자
- 작업의 자체 API 끝점을 호출하는 각 순서에 대해 Accept 및 Decline 단추를 제공합니다.
- 인라인 성공/오류 응답 표시
- 새로 고침 단추 포함
- 사용할 수 있을 만큼 스타일링되어야 합니다(최소 CSS는 괜찮습니다. 런타임에 CORS 문제를 방지하기 위해 외부 CDN 종속성이 없음).
DEMO_UI_SECRET이(가) 설정되지 않은 경우 경고 배너 표시
UI에 표시되는 오류:
Commerce REST 호출이 실패하면 HTTP 상태와 Commerce 오류 본문에 대한 간단한 설명을 포함합니다(sanitize — HTML 태그를 제거하고, 500자로 자름). 자격 증명을 노출하지 마십시오.
응답 도우미:
function jsonResponse(statusCode, obj, extraHeaders = {}) { ... }
function htmlResponse(html) { ... }
.env.example
# Commerce REST base URL — no trailing slash
COMMERCE_BASE_URL=
# OAuth 1.0a integration credentials (Admin → System → Integrations)
COMMERCE_CONSUMER_KEY=
COMMERCE_CONSUMER_SECRET=
COMMERCE_ACCESS_TOKEN=
COMMERCE_ACCESS_TOKEN_SECRET=
# PoC threshold — must match split_payment/general/threshold in Commerce (default: 100)
PAYMENT_THRESHOLD=100
LOG_LEVEL=info
# Demo dashboard optional shared secret
DEMO_UI_SECRET=
DEMO_UI_BASE_URL=
Deploy 명령
split-payment-orchestrator/ 디렉터리에서 모든 파일을 생성한 후:
npm install
cp .env.example .env
# Edit .env with your credentials
aio app deploy
배포 후 aio app deploy이(가) 인쇄한 작업 URL을 확인합니다. demo-dashboard URL에서 연산자 대시보드에 액세스합니다.