{"openapi":"3.0.3","info":{"title":"SoftPay Merchant API","version":"1.0.0","description":"가맹점 청구 · 인텐트 · 키 · 웹훅. SoftPay PaymentConnector만 — SoftAgent credit는 SoftPG product.\n\nCORS: production uses ALLOWED_ORIGINS whitelist; unset → browser direct calls blocked — use server-side MerchantClient.\nRate limit: POST /v1/charges and refund — 60/min/key (process-local). Ready: GET /ready probes DB+runtime.\nEdge: timeout ≠ failed (OUTCOME_PENDING / ExecutionHonesty on custom charge). Money: integer amountMoney.\nDocs: docs/MERCHANT.md · docs/COMPETITIVE_EDGE.md · docs/KEYS.md"},"servers":[{"url":"https://merchant-api-production-a575.up.railway.app"}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-Api-Key"}},"schemas":{"MoneyAmount":{"type":"object","description":"SoftPay Money v2 integer minor wire","properties":{"amount":{"type":"string","description":"Integer minor string"},"value":{"type":"string","description":"Alias of amount"},"decimals":{"type":"integer","minimum":0,"maximum":18},"currency":{"type":"string"}},"required":["decimals","currency"]},"PaymentIntent":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["CREATED","APPROVAL_REQUIRED","APPROVED","OUTCOME_PENDING","ACTION_REQUIRED","SETTLED","FAILED","REFUNDED","CLEARED","REJECTED"],"description":"OUTCOME_PENDING = indeterminate (timeout≠failed). Do not fulfill or re-charge."},"amount":{"type":"number","description":"Derived major — prefer amountMoney"},"amountMoney":{"$ref":"#/components/schemas/MoneyAmount"},"currency":{"type":"string"},"purpose":{"type":"string"}}},"ExecutionHonesty":{"type":"object","description":"Returned on custom charge (intents/run) and approve/execute — timeout≠failed","properties":{"indeterminate":{"type":"boolean"},"requiresAction":{"type":"boolean"},"failed":{"type":"boolean"},"code":{"type":"string","enum":["OUTCOME_PENDING","ACTION_REQUIRED","EXECUTION_FAILED"]},"recovery":{"type":"string","enum":["PROVIDER_STATUS_LOOKUP","COMPLETE_CLIENT_ACTION_THEN_RESUME"]},"principle":{"type":"string","enum":["timeout_neq_failed"]}}},"ChargeRequest":{"type":"object","properties":{"productId":{"type":"string"},"itemId":{"type":"string"},"amount":{"type":"integer","deprecated":true,"description":"Legacy integer major only (float rejected). Prefer amountMoney. Rejected when MERCHANT_MONEY_STRICT=1."},"amountMoney":{"$ref":"#/components/schemas/MoneyAmount"},"purpose":{"type":"string"},"payerId":{"type":"string"},"actorType":{"type":"string","enum":["human","agent","workflow"]},"actorId":{"type":"string"},"embed":{"type":"boolean","description":"true면 paymentPageUrl(embed checkout) 반환"},"express":{"type":"boolean","description":"SoftPay Express 원탭"},"returnUrl":{"type":"string","format":"uri"},"billingMeter":{"type":"boolean","description":"Usage Billing Profile opt-in (SoftAccess first consumer)"},"billingAccountId":{"type":"string"},"billingMode":{"type":"string","enum":["FREE","BYOK","MANAGED_CREDIT","ENTERPRISE_CONTRACT"]}}},"ApiError":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string","enum":["UNAUTHORIZED","VALIDATION_ERROR","NOT_FOUND","FORBIDDEN","RATE_LIMITED","INVALID_SIGNATURE","IDEMPOTENCY_CONFLICT"],"description":"Stable client-branch codes (+ runtime passthrough codes)"},"hint":{"type":"string"}},"required":["error"]},"WebhookEvent":{"type":"object","description":"SoftPay → merchant delivery envelope (see docs/INTEGRATION.md)","properties":{"type":{"type":"string","description":"intent.settled | settlement.completed | payment.refunded | usage_billing.* | …"},"intentId":{"type":"string"},"payload":{"type":"object","additionalProperties":true}}}}},"security":[{"ApiKey":[]}],"paths":{"/health":{"get":{"security":[],"summary":"Liveness","responses":{"200":{"description":"ok"}}}},"/ready":{"get":{"security":[],"summary":"Readiness — DB + runtime health probe","description":"ok=true only when db and runtime are reachable. Liveness remains GET /health. Rate-limit honesty field is informational.","responses":{"200":{"description":"ready","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"db":{"type":"boolean"},"runtime":{"type":"boolean"},"rateLimitHonesty":{"type":"string"},"docs":{"type":"string"}}}}}},"503":{"description":"db or runtime unavailable","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"db":{"type":"boolean"},"runtime":{"type":"boolean"}}}}}}}}},"/v1/openapi.json":{"get":{"security":[],"summary":"This document","responses":{"200":{"description":"OpenAPI JSON"}}}},"/v1/me":{"get":{"summary":"Current merchant","responses":{"200":{"description":"merchant"},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/products":{"get":{"summary":"Catalog for this merchant","responses":{"200":{"description":"products + storeItems"}}}},"/v1/charges":{"post":{"summary":"Create charge / checkout intent","description":"productId/itemId → paymentPageUrl (201). custom amountMoney → runtime Intent (Idempotency-Key forwarded). Rate limit: 60 writes/min/key (process-local).","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Forwarded to runtime for custom amountMoney charges (409 on conflict)"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChargeRequest"}}}},"responses":{"201":{"description":"product/store_item: paymentPageUrl · custom: intent + ExecutionHonesty (timeout≠failed)","content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"chargeType":{"type":"string","enum":["product","store_item","custom"]},"paymentPageUrl":{"type":"string","format":"uri"},"intent":{"$ref":"#/components/schemas/PaymentIntent"},"checkout":{"type":"object","properties":{"intent":{"$ref":"#/components/schemas/PaymentIntent"}}}}},{"$ref":"#/components/schemas/ExecutionHonesty"}]}}}},"400":{"description":"validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/intents":{"get":{"summary":"List intents","parameters":[{"name":"limit","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"intents","content":{"application/json":{"schema":{"type":"object","properties":{"intents":{"type":"array","items":{"$ref":"#/components/schemas/PaymentIntent"}}}}}}}}}},"/v1/intents/{id}":{"get":{"summary":"Get intent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"intent","content":{"application/json":{"schema":{"type":"object","properties":{"intent":{"$ref":"#/components/schemas/PaymentIntent"}}}}}},"403":{"description":"wrong merchant"}}}},"/v1/stats":{"get":{"summary":"Dashboard stats","responses":{"200":{"description":"stats"}}}},"/v1/webhooks/received":{"get":{"summary":"Recent webhook deliveries (inbox)","responses":{"200":{"description":"list"}}}},"/v1/webhooks/url":{"put":{"summary":"Register merchant webhook URL","description":"Registers URL with SoftPay runtime subscribers. Deliveries: X-SoftPay-Signature (HMAC-SHA256), X-SoftPay-Delivery-Id, X-SoftPay-Event. Verify via @softpay/agent-sdk verifyWebhookSignature. docs/MERCHANT.md · docs/WEBHOOK_OUTBOX.md","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri"}}}}}},"responses":{"200":{"description":"ok + webhookUrl"},"400":{"description":"invalid url","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/embed-snippet":{"get":{"summary":"Copy-paste embed snippets for a product/item","description":"SSOT: @softpay/site-config getAttachSnippets (origin check + SOFTPAY_EMBED_ERROR). Same as Console/Demo payment-links.","parameters":[{"name":"productId","in":"query","schema":{"type":"string"}},{"name":"itemId","in":"query","schema":{"type":"string"}},{"name":"express","in":"query","schema":{"type":"string"}},{"name":"returnUrl","in":"query","schema":{"type":"string"}},{"name":"billingMeter","in":"query","schema":{"type":"string"}},{"name":"billingAccountId","in":"query","schema":{"type":"string"}},{"name":"billingMode","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"embedUrl + iframeHtml + widgetJs + curl + merchantClientTs"},"400":{"description":"validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"401":{"description":"unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"404":{"description":"product/item not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/intents/{id}/refund":{"post":{"summary":"Refund settled intent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"Forwarded to runtime (409 on conflict)"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"},"recipient":{"type":"string","description":"EVM Gateway.refund encode recipient (0x)"},"paymentId":{"type":"string"},"gatewayAddress":{"type":"string"}}}}}},"responses":{"200":{"description":"ledger refund + optional usageBillingReverse / evmRefundEncode","content":{"application/json":{"schema":{"type":"object","properties":{"intent":{"$ref":"#/components/schemas/PaymentIntent"},"refundedAmount":{"type":"number","description":"Derived major — prefer refundedAmountMoney"},"refundedAmountMoney":{"$ref":"#/components/schemas/MoneyAmount"},"honesty":{"type":"string","description":"ledger_refunded | ledger_refunded_plus_evm_encode_evidence"},"usageBillingReverse":{"type":"object","nullable":true,"properties":{"honesty":{"type":"string","enum":["intent_refund_profile_reverse","usage_billing_reverse_failed","usage_billing_reverse_skipped_hold_released","usage_billing_reverse_skipped_capture_failed","usage_billing_reverse_skipped_no_credit_capture","usage_billing_reverse_skipped_no_reservation","usage_billing_reverse_skipped_no_hook"]},"reservationId":{"type":"string"}}},"evmRefundEncode":{"type":"object","nullable":true,"description":"Gateway.refund encode-only evidence (submit = Human)","properties":{"honesty":{"type":"string"},"actionRequired":{"type":"boolean","description":"true when operator must broadcast encoded callData"},"callData":{"type":"string"},"recipient":{"type":"string"}}}}}}}},"400":{"description":"validation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"403":{"description":"wrong merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"409":{"description":"idempotency conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"429":{"description":"rate limited","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}}}}},"/v1/keys":{"get":{"summary":"List API keys (metadata)","responses":{"200":{"description":"keys"}}}},"/v1/keys/rotate":{"post":{"summary":"Rotate API key","responses":{"200":{"description":"new apiKey once"}}}},"/v1/keys/revoke":{"post":{"summary":"Revoke API key","responses":{"200":{"description":"ok"}}}},"/v1/keys/events":{"get":{"summary":"API key audit events","responses":{"200":{"description":"events"}}}}}}