intent와 권한
intent는 서비스가 지갑에 “이것을 실행해 달라”고 내미는 JSON입니다. 지금 실행할 호출, 그 호출이 쓰는 금액, 그리고 그 호출이 속한 권한(mandate) 을 담습니다. 사용자는 권한을 한 번 승인합니다. 같은 권한 안의 다음 intent와 서비스 청구는 묻지 않고 실행됩니다. AP2의 intent와 mandate 구조를 따릅니다.
세 가지 종류
섹션 제목: “세 가지 종류”action |
권한 | 예 |
|---|---|---|
purchase |
1회. 상품과 가격을 eq 조건으로 고정. 나중 청구 없음 |
영화표 한 장 |
mandate |
여러 번. 1회 한도, 전체 한도, 횟수, 기간, 주기 한도 | 구독, 예매 예약, 검색 이용료, 예금 운용 |
mandate.update |
같은 권한의 한도, 횟수, 기간만 늘림. 범위는 그대로 | 구독 연장 |
예: 월 구독
섹션 제목: “예: 월 구독”{ "type": "agentpay.intent", "version": 1, "id": "int_8cVd0Hq3pTzR1b2N", "service": "acme-news", "action": "mandate", "summary": "월 구독 월 9,900원, 최대 12개월", "spend_okrw": 9900, "prefund_okrw": 9900, "expires_at": 1792051200, "calls": [ { "target": "0x…token", "value": "0", "data": "0x095ea7b3…" }, { "target": "0x…merchant", "value": "0", "data": "0x…" } ], "mandate": { "key": "acme-news:plan", "label": "Acme 뉴스 월 구독", "total_okrw": 118800, "per_okrw": 9900, "approve_cap_okrw": 9900, "calls": 12, "days": 360, "merchant_charges": true, "period": { "seconds": 2592000, "max_runs": 1 }, "permissions": [{ "target": "0x…merchant", "signature": "pay(bytes32,uint256,uint256)", "predicates": [{ "arg": 1, "op": "eq", "uint": "2" }, { "arg": 2, "op": "lte", "okrw": 9900 }] }] }}intent 필드
섹션 제목: “intent 필드”| 필드 | 형식 | 규칙 |
|---|---|---|
type, version |
"agentpay.intent", 1 |
고정 |
id |
int_ + 8 ~ 64자 |
키트가 만듦 |
service |
서비스 id | intent를 가져온 origin의 서비스와 같아야 함 |
summary |
문자열 | 160자 이하. 승인 화면과 내역에 표시 |
spend_okrw |
정수 | 지금 호출이 쓰는 금액. per_okrw 이하 |
prefund_okrw |
정수, 선택 | 지갑이 이 권한을 위해 갖고 있어야 할 잔액 (충전 안내) |
expires_at |
unix 초 | 지금부터 1시간 이내. 키트 기본은 15분 |
calls |
0 ~ 8개 | { target, value: "0", data }. 모두 권한 안에 들어가야 함 |
금액(*_okrw)은 결제 토큰의 정수 단위입니다. 지갑이 10 ** decimals를 곱해 토큰 최소 단위로 변환합니다.
권한(mandate) 필드
섹션 제목: “권한(mandate) 필드”| 필드 | 기본 | 규칙 |
|---|---|---|
key |
<서비스 id>:로 시작. 같은 key의 살아 있는 권한은 재사용 |
|
label |
80자 이하. 승인 화면 제목 | |
total_okrw, per_okrw |
전체 한도와 1회 한도. per_okrw ≤ total_okrw |
|
approve_cap_okrw |
결제 토큰 approve 1회 최대. total_okrw 이하 |
|
calls |
100 | 실행 횟수 1 ~ 1000 |
days |
30 | 유효 기간 1 ~ 365일 |
valid_after |
승인 시각 | 시작 시각 (예매 예약). 최대 300일 뒤 |
merchant_charges |
false |
서비스가 나중에 청구할 수 있음. 레지스트리에서 허용된 서비스만 |
period |
없음 | { seconds, max_runs?, max_spend_okrw? }. validAfter부터 고정 길이 창 |
permissions |
1 ~ 16개. 등록된 자기 컨트랙트의 함수만 |
조건(predicate)
섹션 제목: “조건(predicate)”permissions[].predicates는 함수 인자 하나(32바이트 단어)에 거는 조건입니다. permissions 항목 하나에 최대 4개를 지정합니다.
| 필드 | 뜻 |
|---|---|
arg |
0부터 세는 인자 위치 |
op |
eq(같음) 또는 lte(이하) |
| 값 하나 | okrw(토큰 단위, 10 ** decimals를 곱해 변환), uint, bytes32, address |
purchase는 최소 하나의 eq 조건으로 상품을 고정해야 합니다.
권한 변경 (mandate.update)
섹션 제목: “권한 변경 (mandate.update)”{ "action": "mandate.update", "spend_okrw": 0, "calls": [], "mandate": { "key": "acme-news:plan" }, "update": { "add_total_okrw": 29700, "add_calls": 3, "add_days": 90 } }변경할 때는 호출을 실행하지 않습니다. add_total_okrw, add_calls, add_days는 각 한도를 늘리고, 선택 항목 per_okrw는 1회 한도를 올립니다. 대상 컨트랙트, 함수, 인자 조건과 주기 한도는 바뀌지 않으며 현재 주기의 사용량도 초기화되지 않습니다. 다른 범위나 주기에는 새 권한이 필요합니다. 이용자는 change_mandate로 변경 내용을 확인하고 승인합니다.
서비스 청구 요청
섹션 제목: “서비스 청구 요청”사용자가 없는 결제입니다. 서비스가 게이트웨이에 POST /merchant/charges로 보냅니다.
{ "mandate_id": "mnd_…", "charge_id": "sub_123-w1", "summary": "월 구독 2회차", "spend_okrw": 9900, "calls": [ … ] }Authorization: AgentPay-Mandate-HMAC id=mcr_…,ts=1792051200,sig=…서명은 METHOD\npath\nts\nsha256(body)에 대한 HMAC-SHA256입니다. 요청 시각과 검증 시각의 차이는 앞뒤로 최대 300초(5분)까지 허용합니다. 키트의 kit.merchant.charge가 처리합니다.
검증 오류
섹션 제목: “검증 오류”| 코드 | 뜻 |
|---|---|
INTENT_SERVICE_MISMATCH |
intent의 service가 가져온 origin의 서비스와 다름 |
CONTRACT_NOT_REGISTERED |
권한 대상이 그 서비스에 등록된 컨트랙트가 아님 |
INTENT_CALL_OUTSIDE_MANDATE |
지금 실행할 호출이 권한 범위 밖 |
MERCHANT_CHARGES_NOT_ALLOWED |
나중 청구가 허용되지 않은 서비스 |
INTENT_EXPIRED |
만료됐거나 1시간보다 길게 잡음 |
OUTSIDE_MANDATE, OVER_LIMIT, PERIOD_LIMIT |
청구가 범위, 한도, 주기 한도를 넘음 |
MANDATE_REVOKED, MANDATE_RELEASED, MANDATE_EXPIRED |
권한이 꺼졌거나 끝남 |
CHARGE_ID_REUSED |
같은 charge_id를 다른 내용으로 다시 보냄 |