개요
CHAI는 사용자의 브라우저상에서 실행되는 결제용 Javascript와 결제 승인/취소용 RESTful API로 구성되어 있습니다. 간단한 몇가지 절차를 통해 CHAI를 연동하실 수 있습니다.
키 발급
CHAI를 이용하려면 API 키를 발급받아야 합니다. 현재는 CHAI와 계약을 통해서만 발급받으실 수 있습니다.
가맹점 등록 후 공개키(public key)와 비밀키(private key) 두 가지의 키가 발급됩니다.
공개키(public key)
공개키는 클라이언트 CHAI SDK 초기화에 사용합니다. 호출할 수 있는 API가 제한되어있습니다.
비밀키(private key)
비밀키로는 모든 API 호출이 가능합니다. 서버 사이드에서만 사용해야하며, 키가 유출되지 않도록 보안에 각별한 주의가 필요합니다.
단건 결제
차이의 결제는 다음과 같은 플로우로 이루어져 있습니다.
1) 가맹점 서버에서 CHAI API를 호출하여 결제 생성(Create)
2) 가맹점 웹페이지에서 CHAI Javascript를 통해 CHAI 결제페이지를 호출
2-1) CHAI 결제페이지에서 결제내역 확인(Prepare) 및 결제 비밀번호 입력(Approve)
3) 가맹점 결제완료 웹페이지(returnUrl)에서 CHAI API를 호출하여 결제승인(Confirm)
1. 결제 생성
생성 API의 정의를 참고해 서버 사이드에서 생성하세요. 응답값에 있는 paymentId와 publicAPIKey, idempotencyKey, returnUrl 총 네개의 값을 CHAI 결제 페이지로 전달해야 합니다.
2. CHAI 결제 페이지 호출
- 결제가 필요한 웹 페이지에 CHAI용
script
태그를 추가합니다. ChaiPayment.checkout
함수를 호출하여 CHAI 결제 페이지로 이동하면 CHAI 앱에서 결제가 진행됩니다.
Import CHAI javascript
<script
type="text/javascript"
src="https://chai.finance/js/v1/payment.min.js">
</script>
Create instance and call ChaiPayment.checkout()
<script type="text/javascript">
ChaiPayment.checkout({
// mode: 'production' // default: staging
publicAPIKey: '<발급 받은 Public API Key>',
paymentId: '<생성 결과에서 전달된 값>',
returnUrl: '<결제 후 돌아올 가맹점 결제 페이지 url>',
idempotencyKey: '<가맹점 주문 번호>',
});
</script>
ChaiPayment.checkout
Parameters
Key | Type | Description | Required | Max Length |
---|---|---|---|---|
publicAPIKey | String | 발급 받은 Public API Key |
Y | 100 |
paymentId | String | 서버에서 결제 생성 결과에 있는 paymentId | Y | 100 |
returnUrl | String | 결제 후 돌아올 가맹점 결제 페이지 url | Y | 500 |
idempotencyKey | String | 가맹점 주문 번호 (고유번호, 중복 결제 방지 등) | Y | 100 |
mode | String | 개발용: 'staging', 실서버: 'prod' | N | 100 |
3. 결제 승인
app.get('/payment/complete', async (req, res) => {
res.writeHead(200, {
'content-type': 'application/json; charset=utf-8'
});
const { paymentId, idempotencyKey, status, code } = req.query;
let payment;
// Payment를 만들지 못하면 paymentId, idempotencyKey가 전달되지 않는다.
if (!paymentId || !idempotencyKey) {
res.end(JSON.stringify({ status, code }));
return;
}
if (status === 'approved') {
// 승인 API 호출
const result = await got({
method: 'POST',
url: `https://api.chai.finance/v1/payment/${paymentId}/confirm`,
headers: {
'Private-API-Key': PRIVATE_API_KEY,
'Idempotency-Key': idempotencyKey
}
});
payment = result.body;
} else {
// 조회 API 호출
const result = await got({
method: 'GET',
url: `https://api.chai.finance/v1/payment/${paymentId}`,
headers: {
'Private-API-Key': PRIVATE_API_KEY,
'Idempotency-Key': idempotencyKey
}
});
payment = result.body;
}
if (payment.status === 'confirmed') {
// TODO: 가맹점 측 주문 완료 처리
} else {
// TODO: 가맹점 측 주문 실패 처리
}
res.end(payment);
})
CHAI 결제 페이지에서 결제가 완료되면 ChaiPayment.checkout
함수 호출시 입력한 returnUrl로 리다이렉트 됩니다. 이 때 paymentId, idempotencyKey, status 값이 query parameter로 함께 리다이렉트 됩니다.
(e.g. *
https://example.io/returnUrl?paymentId=xx&idempotencyKey=yy&status=approved)
해당 페이지의 서버 핸들러에서 CHAI RESTFul API 호출을 통해 결제 결과를 조회하거나 승인 또는 취소할 수 있습니다.
리다이렉트 시 결제 상태 (status)
-
approved : 사용자가 결제를 승인한 상태입니다. 가맹점 웹 애플리케이션 서버 핸들러에서 승인 API를 호출해야 결제가 완료됩니다. 자세한 내용은
승인 API를 참고 하세요.
-
confirmed : 이미 결제가 완료된 상태입니다. 결제 완료 처리하시면 됩니다.
-
user_canceled : 결제 진행 중 사용자가 결제를 취소한 상태입니다.
- failed : 결제 진행 중 특정한 이유로 결제에 실패한 상태입니다.
4. API 정의
결제 상태
결제 상태에는 status와 displayStatus가 있습니다.
Status
Status | Description |
---|---|
waiting | 결제 생성 완료, 사용자가 CHAI 결제 페이지에 오기를 기다리는 중 |
prepared | 사용자가 CHAI 결제 페이지에 도달, 결제 정보 확인 |
approved | 사용자가 결제 완료(결제 비밀번호 입력) |
confirmed | 가맹점 측 결제 승인 완료 (결제 성공) |
user_canceled | 사용자가 결제 진행 중에 취소 |
canceled | 취소된 결제 |
failed | 특정 이유로 결제 실패 |
timeout | 일정 시간 동안 다음 단계로 넘어가지 않고 있는 결제건 자동 취소 |
Display Status
Display Status | Description |
---|---|
partial_confirmed | 부분 취소된 결제 |
생성
curl -X POST https://api.chai.finance/v1/payment \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
-H 'Content-Type: application/json' \
-d '{
"checkoutAmount": 20000,
"description": "테스트 상품",
"returnUrl": "/result"
}'
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "payment",
"status": "waiting",
"displayStatus": "waiting",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"canceledAmount": 0,
"canceledBillingAmount": 0,
"canceledDiscountAmount": 0,
"returnUrl": "/result",
"description": "테스트 상품",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
"metadata": {
"pid": "chai01",
"campaignId": "chai123",
"chaiCouponAmount" : 5000
}
}
HTTP Request
POST api.chai.finance/v1/payment
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Body
Key | Type | Description | Required | Default | Max Length |
---|---|---|---|---|---|
description | String | 결제 상세 정보 (상품명 등) | Y | 500 | |
returnUrl | String | 결제 후 이동될 가맹점 측 페이지 url | Y | 500 | |
checkoutAmount | Number | 결제 금액 | Y | ||
merchantUserId | String | 가맹점 유저 아이디 | 100 | ||
currency | String | 사용할 화폐 (현재 KRW만 지원) | KRW | 3 | |
appScheme | String | 차이앱 결제후 앱복귀를 위한 URL scheme | null | ||
cashReceipt | Boolean | 현금영수증 발급 가능 여부 | true | ||
taxFreeAmount | Number | 복합과세: 결제 금액 중 비과세 금액 | 0 | ||
serviceFeeAmount | Number | 복합과세: 결제 금액 중 봉사료 | 0 | ||
bookShowAmount | Number | 복합과세: 결제 금액 중 도서공연비 | 0 | ||
metadata | Object | 특정 상품의 프로모션 적용 및 비용 트래킹 필드 (id 는 차이와 협의)
{ pid: 'promotion_id', // 해당 상품에 특정 프로모션 적용 campaignId: 'campaign_id', // 파트너사에서 차이쿠폰 적용시 chaiCouponAmount: 5000 // 파트너사에서 차이쿠폰 적용시 } |
현금영수증 발급 가능 여부 (cashReceipt)
현금영수증 발급 가능 여부를 설정할 수 있습니다.
(문화상품권이나 백화점상품권, 모바일 쿠폰 등 과세 대상에서 제외되는 유가증권 등의 상품은 현금영수증 발행 대상에서 제외됩니다)
-
true : 현금영수증 발급 (default)
- false : 현금영수증 발급하지 않음
복합과세 처리
결제 금액 중 비과세/봉사료/도서공연비 처리가 필요할 경우 '복합과세' 파라미터를 설정해야 합니다.
-
taxFreeAmount : 결제 금액 중 '비과세 금액'
-
serviceFeeAmount : 결제 금액 중 '봉사료'
-
bookShowAmount : 결제 금액 중 '도서공연비'
3개의 파라미터 중 하나라도 0이 아닌 값으로 설정하면 '복합과세' 결제로 처리되며, '과세 금액'과 '부가세'는 자동으로 계산됩니다.
복합과세 결제인 경우 taxFreeAmount, serviceFeeAmount, bookShowAmount 중 넣지 않은 값은 0원으로 셋팅됩니다.
위 파라미터를 통해 설정한 값은 현금영수증 발행에 반영됩니다.
복합과세가 아닌 경우, 아래와 같이 처리됩니다.
-
부가세 : (결제 금액 / 11), 소숫점 버림
-
공급가액 : 결제 금액 - 부가세
-
비과세 금액, 봉사료, 도서공연비 : 0
복합과세의 경우 아래와 같이 처리됩니다. 도서공연비가 있는 경우 따로 현금영수증이 발급됩니다.
도서공연비는 비과세 처리됩니다.
-
과세 금액 : 결제 금액 - 비과세 금액 - 도서공연비 - 봉사료
-
부가세 : (과세 금액 / 11), 소숫점 버림
-
공급가액 : 과세 금액 - 부가세 + 비과세 금액
-
봉사료 : 봉사료
-
도서공연비 부가세 : 0
-
도서공연비 공급가액 : 도서공연비
-
도서공연비 비과세 : 도서공연비
-
도서공연비 봉사료 : 0
cashReceipt = false인 경우, 현금영수증을 발급하지 않습니다.
복합 과세 (taxFreeAmount, serviceFeeAmount, bookShowAmount) 금액을 넣어도 셋팅되지 않습니다.
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (payment) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 결제 요청 금액 | |
billingAmount | Number | 실결제 금액 | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 결제 완료 후 돌아갈 페이지 | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
조회
curl -X GET https://api.chai.finance/v1/payment/d22e1f5eab6fe7791725b0da921f665345c08e29 \
-H 'Public-API-Key: ${PUBLIC_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}'
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "payment",
"status": "confirmed",
"displayStatus": "confirmed",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"canceledAmount": 0,
"canceledBillingAmount": 0,
"canceledDiscountAmount": 0,
"returnUrl": "/result",
"description": "테스트 상품",
"merchantUserId": "xxxyyyzzz",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z"
}
HTTP Request
GET api.chai.finance/v1/payment/:paymentId
Parameters
- paymentId: querystring으로 전달된 결제 식별자
Headers
- Public-API-Key: 차이에서 제공한
공개키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (payment) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 결제 요청 금액 | |
discountAmount | Number | 할인 금액 | |
billingAmount | Number | 실결제 금액 | |
chargingAmount | Number | 충전 금액 (잔여 포인트 부족시) | |
canceledAmount | Number | 취소된 금액 (누적) | |
canceledBillingAmount | Number | 취소된 실결제 금액 (누적) | |
canceledDiscountAmount | Number | 취소된 할인 금액 (누적) | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 결제 완료 후 돌아갈 페이지 | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
승인
curl -X POST https://api.chai.finance/v1/payment/d22e1f5eab6fe7791725b0da921f665345c08e29/confirm \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "payment",
"status": "confirmed",
"displayStatus": "confirmed",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"canceledAmount": 0,
"canceledBillingAmount": 0,
"canceledDiscountAmount": 0,
"returnUrl": "/result",
"description": "테스트 상품",
"merchantUserId": "xxxyyyzzz",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
"charge": {
"bankCode": "004",
"bankName": "국민은행",
"accountNumber": "123456789012"
}
}
HTTP Request
POST api.chai.finance/v1/payment/:paymentId/confirm
Timeout
- 20초 설정: 타임아웃시 결제실패 처리
Parameters
- paymentId: querystring으로 전달된 결제 식별자
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (payment) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 결제 요청 금액 | |
discountAmount | Number | 할인 금액 | |
billingAmount | Number | 실결제 금액 | |
chargingAmount | Number | 충전 금액 (잔여 포인트 부족시) | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 결제 완료 후 돌아갈 페이지 | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 | |
charge | Object | 결제 계좌 정보 |
charge Object
결제 시 차이머니 충전이 필요한 경우(chargingAmount > 0) 결제 계좌 정보가 표시됩니다.
Key | Type | Description | Max Length |
---|---|---|---|
bankCode | String | 은행 코드 | 3 |
bankName | String | 은행명 | 100 |
accountNumber | String | 마스킹된 계좌번호 | 100 |
취소
curl -X POST https://api.chai.finance/v1/payment/d22e1f5eab6fe7791725b0da921f665345c08e29/cancel \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
-H 'Content-Type: application/json' \
-d '{
"cancelAmount":10000
}'
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "payment",
"status": "canceled",
"displayStatus": "canceled",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"canceledAmount": 20000,
"canceledBillingAmount": 18000,
"canceledDiscountAmount": 2000,
"returnUrl": "/result",
"description": "테스트 상품",
"merchantUserId": "xxxyyyzzz",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
"transactionResult": {
"canceledAmount": 20000,
"canceledBillingAmount": 18000,
"canceledDiscountAmount": 2000
}
}
HTTP Request
POST api.chai.finance/v1/payment/:paymentId/cancel
Timeout
- 20초 설정: 타임아웃시 취소성공 처리
Parameters
- paymentId: querystring으로 전달된 결제 식별자
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Parameters
Key | Type | Description | Required |
---|---|---|---|
cancelAmount | Number | 취소 금액 | Y |
checkoutAmount | Number | 취소 요청 전 결제 잔액 (중복취소 방지용) | |
taxFreeAmount | Number | 복합과세: 취소 금액 중 비과세 금액 | 복합과세인 경우 Y |
serviceFeeAmount | Number | 복합과세: 취소 금액 중 봉사료 | 복합과세인 경우 Y |
bookShowAmount | Number | 복합과세: 취소 금액 중 도서공연비 | 복합과세인 경우 Y |
취소 요청 금액
취소 요청하는 금액은 0보다 크고, '남은 결제 금액'보다 작거나 같아야 합니다. '과세 금액'과 '부가세'는 자동으로 계산됩니다.
중복 취소 방지
cancel api가 두 번 호출되어 부분 취소가 두 번 일어나는 일을 방지하려면, checkoutAmount(결제 잔액)을 넣어주세요.
checkoutAmount가 0 이상이면 금액 검증 합니다. 해당 값이 취소 요청 전의 잔액과 다르면 idempotencyError를 리턴합니다.
복합과세 결제 취소 처리
결제 생성 시 복합과세 파라미터를 지정했다면, 취소 요청 시에도 반드시 어떤 부분에서 취소 처리할지 지정해야 합니다.
취소 시 taxFreeAmount, serviceFeeAmount, bookShowAmount를 전부 넣어야합니다. (취소할 금액이 없다면 0원으로 셋팅)
환불
결제 취소 시, 결제 계좌 대신 차이 머니로 환불됩니다.
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (payment) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 취소 후 남은 결제 금액 | |
discountAmount | Number | 취소 후 재계산 된 할인 금액 | |
billingAmount | Number | 취소 후 남은 실결제 금액 | |
chargingAmount | Number | 취소 후 남은 충전 금액 | |
canceledAmount | Number | 취소된 금액 (누적) | |
canceledBillingAmount | Number | 취소된 실결제 금액 (누적) | |
canceledDiscountAmount | Number | 취소된 할인 금액 (누적) | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 결제 완료 후 돌아갈 페이지 | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 | |
transactionResult | Object | 트랜잭션 결과 값 |
transactionResult Object
Key | Type | Description | Max Length |
---|---|---|---|
canceledAmount | Number | 해당 트랜잭션에서 취소된 금액 | |
canceledBillingAmount | Number | 해당 트랜잭션에서 취소된 실결제 금액 | |
canceledDiscountAmount | Number | 해당 트랜잭션에서 취소된 할인 금액 |
영수증 조회
curl -X GET https://api.chai.finance/v1/payment/receipt \
-H 'Public-API-Key: ${PUBLIC_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}'
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "payment",
"status": "confirmed",
"displayStatus": "confirmed",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"promotionAmount": 0,
"canceledAmount": 0,
"canceledBillingAmount": 0,
"canceledDiscountAmount": 0,
"returnUrl": "/result",
"description": "테스트 상품",
"merchantUserId": "xxxyyyzzz",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
"merchant": {
"name": "Test Merchant",
"businessNumber": "123-45-67890",
"address": "Seoul",
"representativeName": "김차이",
"customerServicePhone": "02-000-0000"
},
"initial": {
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
},
"charge": {
"bankCode": "000",
"bankName": "XX은행",
"accountNumber": "0000"
},
"cashReceipt": {
"approve": [
{
"confirmCode": "F99999999",
"isBookShow": false,
"totalAmount": 18000,
"supplyAmount": 16363,
"vatAmount": 1637,
"cashReceiptType": "consumer_number",
"cashReceiptTypeName": "개인 소득공제",
"cashReceiptNumber": "01012341234"
}
],
"taxFreeAmount": 0,
"bookShowAmount": 0,
"serviceFeeAmount": 0
}
}
HTTP Request
GET api.chai.finance/v1/payment/receipt
Headers
- Public-API-Key: 차이에서 제공한
공개키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (payment) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 결제 금액 | |
discountAmount | Number | 할인 금액 | |
billingAmount | Number | 실결제 금액 | |
chargingAmount | Number | 충전 금액 | |
canceledAmount | Number | 취소된 금액 (누적) | |
canceledBillingAmount | Number | 취소된 실결제 금액 (누적) | |
canceledDiscountAmount | Number | 취소된 할인 금액 (누적) | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 결제 완료 후 돌아갈 페이지 | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 | |
merchant | Object | 가맹점 정보 | |
initial | Object | 최초 결제 정보 | |
charge | Object | 결제 계좌 정보 | |
cashReceipt | Object | 현금영수증 정보 |
merchant Object
Key | Type | Description | Max Length |
---|---|---|---|
name | String | 서비스명 | 100 |
businessNumber | String | 사업자번호 | 100 |
address | String | 주소 | 500 |
representativeName | String | 대표자명 | 100 |
customerServicePhone | String | 고객센터 | 100 |
initial Object
Key | Type | Description | Max Length |
---|---|---|---|
checkoutAmount | Number | 최초 결제 금액 | |
discountAmount | Number | 최초 할인 금액 | |
billingAmount | Number | 최초 실결제 금액 | |
chargingAmount | Number | 최초 충전 금액 |
charge Object
Key | Type | Description | Max Length |
---|---|---|---|
bankCode | String | 은행 코드 | 3 |
bankName | String | 은행명 | 100 |
accountNumber | String | 계좌번호 | 100 |
cashReceipt Object
Key | Type | Description | Max Length |
---|---|---|---|
approve | Object[] | 발급 내역 | |
taxFreeAmount | Number | 비과세 금액 | |
serviceFeeAmount | Number | 봉사료 금액 | |
bookShowAmount | Number | 도서공연비 금액 |
approve Object
Key | Type | Description | Max Length |
---|---|---|---|
confirmCode | String | 승인번호 | 100 |
cashReceiptType | String | 현금영수증 발급 타입 | 50 |
cashReceiptTypeName | String | 현금영수증 발급 타입 텍스트 | 50 |
cashReceiptNumber | String | 마스킹 처리 된 현금영수증 발급 번호 | 50 |
isBookShow | Boolean | 도서공연비 여부 | |
totalAmount | Number | 총 금액 | |
vatAmount | Number | 부가세 | |
supplyAmount | Number | 공급가액 |
은행코드
Code | Bank | Code | Bank | ||
---|---|---|---|---|---|
002 | 산업은행 | 003 | 기업은행 | ||
004 | 국민은행 | 007 | 수협 | ||
011 | 농협 | 020 | 우리은행 | ||
023 | SC 은행 | 031 | 대구은행 | ||
032 | 부산은행 | 034 | 광주은행 | ||
035 | 제주은행 | 037 | 전북은행 | ||
039 | 경남은행 | 045 | 새마을금고 | ||
048 | 신협은행 | 071 | 우체국 | ||
088 | 신한은행 | 089 | K뱅크 |
자동 결제
차이의 자동결제는 다음과 같은 플로우로 이루어져 있습니다.
1) 가맹점 서버에서 CHAI API를 호출하여 자동결제 생성(Create)
2) 가맹점 웹페이지에서 CHAI Javascript를 통해 CHAI 자동결제페이지를 호출
2-1) CHAI 자동결제페이지에서 자동결제내역 확인(Prepare) 및 결제 비밀번호 입력(Approve)
3) 가맹점 자동결제완료 웹페이지(returnUrl)에서 CHAI API를 호출하여 자동결제승인(Confirm)
1. 자동결제 생성
자동결제 생성 API의 정의를 참고해 서버 사이드에서 생성하세요. 응답값에 있는 subscriptionId와 publicAPIKey, idempotencyKey, returnUrl 총 네개의 값을 CHAI 자동결제 등록페이지로 전달해야 합니다.
2. CHAI 자동결제 페이지 호출
- 자동결제 등록이 필요한 웹 페이지에 CHAI용
script
태그를 추가합니다. ChaiPayment.subscribe
함수를 호출하여 CHAI 자동결제 등록페이지로 이동하면 CHAI 앱에서 결제가 진행됩니다.
Import CHAI javascript
<script
type="text/javascript"
src="https://chai.finance/js/v1/payment.min.js">
</script>
Create instance and call ChaiPayment.subscribe()
<script type="text/javascript">
ChaiPayment.subscribe({
// mode: 'production' // default: staging
publicAPIKey: '<발급 받은 Public API Key>',
subscriptionId: '<자동결제 생성 결과에서 전달된 값>',
returnUrl: '<자동결제 등록 후 돌아올 가맹점 등록 페이지 url>',
idempotencyKey: '<가맹점 자동결제 주문 번호>',
});
</script>
ChaiPayment.subscribe
Parameters
Key | Type | Description | Required | Max Length |
---|---|---|---|---|
publicAPIKey | String | 발급 받은 Public API Key |
Y | 100 |
subscriptionId | String | 서버에서 자동결제 생성 결과에 있는 subscriptionId | Y | 100 |
returnUrl | String | 자동결제 등록 후 돌아올 가맹점 결제 페이지 url | Y | 500 |
idempotencyKey | String | 가맹점 주문 번호 (고유번호, 중복 등록 방지 등) | Y | 100 |
mode | String | 개발용: 'staging', 실서버: 'prod' | N | 100 |
3. 자동결제 승인
app.get('/payment/subscription/complete', async (req, res) => {
res.writeHead(200, {
'content-type': 'application/json; charset=utf-8'
});
const { subscriptionId, idempotencyKey, status, code } = req.query;
let subscription;
// Subscription 만들지 못하면 subscriptionId, idempotencyKey가 전달되지 않는다.
if (!subscriptionId || !idempotencyKey) {
res.end(JSON.stringify({ status, code }));
return;
}
if (status === 'approved') {
// 자동결제 등록 승인 API 호출
const result = await got({
method: 'POST',
url: `https://api.chai.finance/v1/payment/subscription/${subscriptionId}/confirm`,
headers: {
'Private-API-Key': PRIVATE_API_KEY,
'Idempotency-Key': idempotencyKey
}
});
subscription = result.body;
} else {
// 자동결제 등록 조회 API 호출
const result = await got({
method: 'GET',
url: `https://api.chai.finance/v1/payment/subscription/${subscriptionId}`,
headers: {
'Public-API-Key': PUBLIC_API_KEY,
'Idempotency-Key': idempotencyKey
}
});
subscription = result.body;
}
if (payment.status === 'confirmed') {
// TODO: 가맹점 측 자동결제 등록 완료 처리
} else {
// TODO: 가맹점 측 자동결제 등록 실패 처리
}
res.end(subscription);
})
CHAI 자동결제 등록 페이지에서 등록이 완료되면 ChaiPayment.subscribe
함수 호출시 입력한 returnUrl로 리다이렉트 됩니다. 이 때 subscriptionId, idempotencyKey, status 값이 query parameter로 함께 리다이렉트 됩니다.
(e.g. *
https://example.io/returnUrl?subscriptionId=xx&idempotencyKey=yy&status=approved)
해당 페이지의 서버 핸들러에서 CHAI RESTFul API 호출을 통해 자동결제 등록 결과를 조회하거나 승인 또는 비활성화할 수 있습니다.
리다이렉트 시 자동결제 등록 상태 (status)
-
approved : 사용자가 자동결제 등록을 승인한 상태입니다. 가맹점 웹 애플리케이션 서버 핸들러에서 자동결제 승인 API를 호출해야 결제가 완료됩니다. 자세한 내용은
자동결제 승인 API를 참고 하세요.
-
confirmed : 자동결제 등록이 완료된 상태입니다. 해당 subscriptionId로 자동결제 결제승인이 가능합니다. 자세한 내용은
자동결제 결제승인 API를 참고 하세요.
-
user_canceled : 자동결제 등록 중 사용자가 등록을 취소한 상태입니다.
- failed : 자동결제 등록 중 특정한 이유로 등록에 실패한 상태입니다.
4. 자동결제 API 정의
자동결제 상태
자동결제 상태에는 status와 displayStatus가 있습니다.
Status
Status | Description |
---|---|
waiting | 자동결제 생성 완료, 사용자가 CHAI 자동결제 등록페이지에 오기를 기다리는 중 |
prepared | 사용자가 CHAI 자동결제 등록페이지에 도달, 결제 정보 확인 |
approved | 사용자가 자동결제 등록완료(결제 비밀번호 입력) |
confirmed | 가맹점 측 자동결제 승인 완료 (자동결제 등록 성공) |
user_canceled | 사용자가 자동결제 등록 중에 취소 |
inactive | 비활성화 된 자동결제 |
failed | 특정 이유로 자동결제 등록실패 |
churn | 일정 시간 동안 다음 단계로 넘어가지 않고 있는 자동결제건 등록 건 |
Display Status
Display Status | Description |
---|
자동결제 생성
curl -X POST https://api.chai.finance/v1/payment/subscription \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
-H 'Content-Type: application/json' \
-d '{
"checkoutAmount": 20000,
"description": "테스트 자동결제 상품",
"returnUrl": "/result"
}'
The above command returns JSON structured like this:
{
"subscriptionId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"status": "waiting",
"displayStatus": "waiting",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"checkoutAmount": 20000,
"returnUrl": "/result",
"description": "테스트 자동결제 상품",
"merchantUserId": "Merchant UserID",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
}
HTTP Request
POST api.chai.finance/v1/payment/subscription
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 등록 방지 식별자(가맹점 자동결제 주문 번호)
Body
Key | Type | Description | Required | Default | Max Length |
---|---|---|---|---|---|
description | String | 자동결결제 상세 정보 (상품명 등) | Y | 500 | |
returnUrl | String | 자동결제 등록 후 이동될 가맹점 측 페이지 url | Y | 500 | |
checkoutAmount | Number | 자동결제 금액 | Y | ||
merchantUserId | String | 가맹점 유저 아이디 | 100 | ||
appScheme | String | 차이앱 자동결제 등록 후 앱복귀를 위한 URL scheme | null | ||
userCI | String | 사용자 인증을 위한 CI 값 | null |
Response
Key | Type | Description | Max Length |
---|---|---|---|
subscriptionId | String | 자동결제 등록 고유 번호 | 100 |
status | String | 자동결제 등록상태 | 50 |
displayStatus | String | 자동결제 등록상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 자동결제 등록고유 식별자 (중복 등록 방지용) | 100 |
checkoutAmount | Number | 자동결제 등록 금액 | |
description | String | 자동결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 자동결제 등록 후 돌아갈 페이지 | 500 |
merchantUserId | String | 가맹점 유저 아이디 | 100 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
자동결제 등록조회
curl -X GET https://api.chai.finance/v1/payment/subscription/d22e1f5eab6fe7791725b0da921f665345c08e29 \
-H 'Public-API-Key: ${PUBLIC_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}'
The above command returns JSON structured like this:
{
"subscriptionId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"status": "confirmed",
"displayStatus": "confirmed",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"checkoutAmount": 20000,
"returnUrl": "/result",
"description": "테스트 자동결제 상품",
"merchantUserId": "Merchant UserID",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
}
HTTP Request
GET api.chai.finance/v1/payment/subscription/:subscriptionId
Parameters
- subscriptionId: querystring으로 전달된 결제 식별자
Headers
- Public-API-Key: 차이에서 제공한
공개키
- Idempotency-Key: 중복 자동결제 등록방지 식별자(가맹점 자동결제 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
subscriptionId | String | 자동결제 등록 고유 번호 | 100 |
status | String | 자동결제 등록상태 | 50 |
displayStatus | String | 자동결제 등록상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 자동결제 등록고유 식별자 (중복 등록 방지용) | 100 |
checkoutAmount | Number | 자동결제 등록 금액 | |
description | String | 자동결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 자동결제 등록 후 돌아갈 페이지 | 500 |
merchantUserId | String | 가맹점 유저 아이디 | 100 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
자동결제 승인
curl -X POST https://api.chai.finance/v1/payment/subscription/d22e1f5eab6fe7791725b0da921f665345c08e29/confirm \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
The above command returns JSON structured like this:
{
"subscriptionId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"status": "confirmed",
"displayStatus": "confirmed",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"checkoutAmount": 20000,
"returnUrl": "/result",
"description": "테스트 자동결제 상품",
"merchantUserId": "Merchant UserID",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
}
HTTP Request
POST api.chai.finance/v1/payment/subscription/:subscriptionId/confirm
Parameters
- subscriptionId: querystring으로 전달된 결제 식별자
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 자동결제 등록방지 식별자(가맹점 자동결제 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
subscriptionId | String | 자동결제 등록 고유 번호 | 100 |
status | String | 자동결제 등록상태 | 50 |
displayStatus | String | 자동결제 등록상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 자동결제 등록고유 식별자 (중복 등록 방지용) | 100 |
checkoutAmount | Number | 자동결제 등록 금액 | |
description | String | 자동결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 자동결제 등록 후 돌아갈 페이지 | 500 |
merchantUserId | String | 가맹점 유저 아이디 | 100 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
자동결제 비활성화
curl -X POST https://api.chai.finance/v1/payment/subscription/d22e1f5eab6fe7791725b0da921f665345c08e29/inactive \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}'
The above command returns JSON structured like this:
{
"subscriptionId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"status": "inactive",
"displayStatus": "inactive",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"checkoutAmount": 20000,
"returnUrl": "/result",
"description": "테스트 자동결제 상품",
"merchantUserId": "Merchant UserID",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
}
HTTP Request
POST api.chai.finance/v1/payment/subscription/:subscriptionId/inactive
Parameters
- subscriptionId: querystring으로 전달된 결제 식별자
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 자동결제 등록방지 식별자(가맹점 자동결제 주문 번호)
Response
Key | Type | Description | Max Length |
---|---|---|---|
subscriptionId | String | 자동결제 등록 고유 번호 | 100 |
status | String | 자동결제 등록상태 | 50 |
displayStatus | String | 자동결제 등록상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 자동결제 등록고유 식별자 (중복 등록 방지용) | 100 |
checkoutAmount | Number | 자동결제 등록 금액 | |
description | String | 자동결제 상세 정보 (상품명 등) | 500 |
returnUrl | String | 자동결제 등록 후 돌아갈 페이지 | 500 |
merchantUserId | String | 가맹점 유저 아이디 | 100 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
자동결제 결제승인
curl -X POST https://api.chai.finance/v1/payment/subscription/:subscriptionId/purchase \
-H 'Private-API-Key: ${PRIVATE_API_KEY}' \
-H 'Idempotency-Key: ${IDEMPOTENCY_KEY}' \
-H 'Content-Type: application/json' \
-d '{
"checkoutAmount": 20000,
"description": "자동결제 테스트 상품 1회차",
}'
The above command returns JSON structured like this:
{
"paymentId": "d22e1f5eab6fe7791725b0da921f665345c08e29",
"type": "subscription",
"status": "confirm,
"displayStatus": "confirm",
"idempotencyKey": "af9a88a8-109f-4513-989b-12f2fb20a301",
"currency": "KRW",
"checkoutAmount": 20000,
"discountAmount": 2000,
"billingAmount": 18000,
"chargingAmount": 18000,
"canceledAmount": 0,
"canceledBillingAmount": 0,
"canceledDiscountAmount": 0,
"description": "테스트 상품",
"createdAt": "2018-12-18T05:14:22.337Z",
"updatedAt": "2018-12-18T05:14:22.337Z",
"metadata": {
"pid": "chai01",
"campaignId": "chai123",
"chaiCouponAmount" : 5000
}
}
HTTP Request
POST api.chai.finance/v1/payment/subscription/:subscriptionId/purchase
Parameters
- subscriptionId: querystring으로 전달된 결제 식별자
Headers
- Private-API-Key: 차이에서 제공한
비밀키
- Idempotency-Key: 중복 결제 방지 식별자(가맹점 주문 번호)
Body
Key | Type | Description | Required | Default | Max Length |
---|---|---|---|---|---|
description | String | 결제 상세 정보 (상품명 등) | Y | 500 | |
checkoutAmount | Number | 결제 금액 | Y | ||
merchantUserId | String | 가맹점 유저 아이디 | 100 | ||
currency | String | 사용할 화폐 (현재 KRW만 지원) | KRW | 3 | |
cashReceipt | Boolean | 현금영수증 발급 가능 여부 | true | ||
taxFreeAmount | Number | 복합과세: 결제 금액 중 비과세 금액 | 0 | ||
serviceFeeAmount | Number | 복합과세: 결제 금액 중 봉사료 | 0 | ||
bookShowAmount | Number | 복합과세: 결제 금액 중 도서공연비 | 0 | ||
metadata | Object | 특정 상품의 프로모션 적용 및 비용 트래킹 필드 (id 는 차이와 협의)
{ pid: 'promotion_id', // 해당 상품에 특정 프로모션 적용 campaignId: 'campaign_id', // 파트너사에서 차이쿠폰 적용시 chaiCouponAmount: 5000 // 파트너사에서 차이쿠폰 적용시 } |
현금영수증 발급 가능 여부 (cashReceipt)
현금영수증 발급 가능 여부를 설정할 수 있습니다.
(문화상품권이나 백화점상품권, 모바일 쿠폰 등 과세 대상에서 제외되는 유가증권 등의 상품은 현금영수증 발행 대상에서 제외됩니다)
-
true : 현금영수증 발급 (default)
- false : 현금영수증 발급하지 않음
복합과세 처리
결제 금액 중 비과세/봉사료/도서공연비 처리가 필요할 경우 '복합과세' 파라미터를 설정해야 합니다.
-
taxFreeAmount : 결제 금액 중 '비과세 금액'
-
serviceFeeAmount : 결제 금액 중 '봉사료'
-
bookShowAmount : 결제 금액 중 '도서공연비'
3개의 파라미터 중 하나라도 0이 아닌 값으로 설정하면 '복합과세' 결제로 처리되며, '과세 금액'과 '부가세'는 자동으로 계산됩니다.
복합과세 결제인 경우 taxFreeAmount, serviceFeeAmount, bookShowAmount 중 넣지 않은 값은 0원으로 셋팅됩니다.
위 파라미터를 통해 설정한 값은 현금영수증 발행에 반영됩니다.
복합과세가 아닌 경우, 아래와 같이 처리됩니다.
-
부가세 : (결제 금액 / 11), 소숫점 버림
-
공급가액 : 결제 금액 - 부가세
-
비과세 금액, 봉사료, 도서공연비 : 0
복합과세의 경우 아래와 같이 처리됩니다. 도서공연비가 있는 경우 따로 현금영수증이 발급됩니다.
도서공연비는 비과세 처리됩니다.
-
과세 금액 : 결제 금액 - 비과세 금액 - 도서공연비 - 봉사료
-
부가세 : (과세 금액 / 11), 소숫점 버림
-
공급가액 : 과세 금액 - 부가세 + 비과세 금액
-
봉사료 : 봉사료
-
도서공연비 부가세 : 0
-
도서공연비 공급가액 : 도서공연비
-
도서공연비 비과세 : 도서공연비
-
도서공연비 봉사료 : 0
cashReceipt = false인 경우, 현금영수증을 발급하지 않습니다.
복합 과세 (taxFreeAmount, serviceFeeAmount, bookShowAmount) 금액을 넣어도 셋팅되지 않습니다.
Response
Key | Type | Description | Max Length |
---|---|---|---|
paymentId | String | 결제 고유 번호 | 100 |
type | String | 결제 타입 (subscription) | 50 |
status | String | 결제 상태 | 50 |
displayStatus | String | 결제 상태 (상세) | 50 |
idempotencyKey | String | 가맹점 측 결제 고유 식별자 (중복 결제 방지용) | 100 |
currency | String | 사용할 화폐 (현재 KRW만 지원) | 3 |
checkoutAmount | Number | 결제 요청 금액 | |
billingAmount | Number | 실결제 금액 | |
description | String | 결제 상세 정보 (상품명 등) | 500 |
createdAt | Date | 결제 생성일 | |
updatedAt | Date | 결제 최종 갱신일 |
앱 연동 가이드
차이 앱 결제는 가맹점의 웹뷰에서 브릿지 페이지를 노출하고, 사용자가 브릿지 페이지의 결제 버튼을 클릭하여 차이 앱으로 이동해 결제 승인 후 다시 돌아와 해당 페이지에서 결제를 완료하는 구조로 되어있습니다.
차이 앱이 동작 가능한 버전은
Android 5.1(LOLLIPOP_MR1, API Level 22)
이상,
iOS 9
이상입니다.
Android
webview.webViewClient = object : WebViewClient() {
// API level 24 이상에서 사용
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
if (request?.url != null) {
return shouldOverrideUrlLoading(request.url)
}
return false
}
// API level 24 미만에서 사용
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
if (url != null) {
val uri = Uri.parse(url)
return shouldOverrideUrlLoading(uri)
}
return false
}
fun shouldOverrideUrlLoading(uri: Uri): Boolean {
// 차이 앱에서 사용하는 chaipayment:// market:// 이 여기에서 처리됩니다.
// 다른 PG나 특정 앱 URL이 들어온다면 조건을 더 추가해서 처리해주세요
if (!listOf("http", "https").contains(uri.scheme)) {
return try {
val intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME)
startActivity(intent)
// 액티비티가 시작 되었으므로 true를 리턴하여
// 웹뷰에서 해당 페이지로 이동하지 않습니다.
true
} catch (e: URISyntaxException) {
e.printStackTrace()
false
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
// chaipayment:// 호출 시 차이 앱이 없다면 이곳이 실행되지만
// 숨겨진 iframe 내부에서 에러 페이지가 뜨기 때문에(화면에 노출되지 않음)
// 따로 핸들링 하지 않아도 무관합니다.
// if (uri.scheme != null && uri.scheme == "chaipayment") true
false
}
}
return false
}
}
-
안드로이드 어플리케이션에서 WebView를 만들어 전달해드린 브릿지 페이지 링크로 이동합니다.
-
WebViewClient를 상속받는 클래스를 구현하고 클래스 내 아래 두 함수를 오버라이드합니다.
구현 목적은 웹뷰 내에서 이동하려는 URI의 scheme이 chaipayment://
또는 market://
일 경우 웹뷰에서 노출하는 대신 각각의 액티비티를 생성해 이동하는 것입니다. 구현하지 않을 경우 차이 앱, 마켓으로 연결되지 않으며 웹뷰에 오류페이지가 노출될 수 있습니다.
상세한 내용은 코드에서 설명하니, 코드를 참고해 알맞게 구현해주면 됩니다.
- 결제 후 차이 액티비티가 닫히면 웹뷰로 돌아와 나머지 처리가 진행되므로, 차이 앱 또는 마켓 앱이 뜬 뒤에도 해당 웹뷰를 닫지 않고 유지해야합니다.
iOS
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
}
if url.absoluteString.range(of: "//itunes.apple.com/") != nil {
UIApplication.shared.openURL(url)
// 웹뷰에서 직접 URL을 열지 않도록 네비게이션을 취소합니다.
decisionHandler(.cancel)
return
}
// 다른 PG나 특정 앱 URL이 들어온다면 조건을 더 추가해서 처리해주세요
// else if !url.absoluteString.hasPrefix("http://") && !url.absoluteString.hasPrefix("https://") {
// if UIApplication.shared.canOpenURL(url) {
// UIApplication.shared.openURL(url)
// decisionHandler(.cancel)
// return
// }
// }
}
- 아이폰 어플리케이션에서 WKWebView를 만들고, 전달드린 브릿지 페이지 링크로 차이 결제 페이지로 이동합니다.
- WKWebView에서는 앱스토어 링크가 더 이상 동작하지 않기 때문에 해당 링크를 직접 앱스토어로 보내주는 구현이 필요합니다. WKNavigationDelegate를 구현하는 클래스를 구현하고, 아래 메소드를 오버라이드합니다.
상세한 내용은 코드에서 설명하니, 코드를 참조해 알맞게 구현해주면 됩니다.
- 결제 후 차이 액티비티가 닫히면 웹뷰로 돌아와 나머지 처리가 진행되므로, 차이 앱 또는 마켓 앱이 뜬 뒤에도 해당 웹뷰를 닫지 않고 유지해야합니다.
Error 정의
에러 핸들링
400 Bad Request
{
"type": "INVALID_REQUEST_ERROR",
}
403 Forbidden
{
"type": "PAYMENT_ERROR",
"message": "잔액이 부족합니다. 계좌를 확인해주세요",
"code": "P004"
}
555 Server Error
{
"type": "API_ERROR",
"code": "S201"
}
HTTP(S) status code 200은 성공, 4XX는 실패, 5XX는 서버 또는 네트워크 관련 에러로 처리합니다.
- 200 - 성공시 해당 API의 Response에 정의된 내용으로 JSON Object를 응답합니다.
- 4XX - type, code(옵션), message(옵션)로 구성된 JSON Object를 응답합니다.
- 5XX - type, code로 구성된 JSON Object를 응답합니다.
Code | Name | Description |
---|---|---|
200 | OK | 정상적으로 요청이 완료됨 |
400 | Bad Request | 요청을 처리할 수 없음. 보통 필수 파라미터를 입력하지 않았을 때 발생 |
401 | Unauthorized | 유효한 API key가 제공되지 않았거나 권한이 부족함 |
402 | Request Failed | 파라미터는 제대로 입력됐으나 요청을 처리할 수 없음 |
403 | Forbidden | 요청 거부. 권한 없음 |
404 | Not Found | 요청한 주소에 리소스 존재하지 않음 (없는 페이지) |
409 | Conflict | 요청이 다른 요청과 충돌함 (idempotency key가 중복된 경우) |
429 | Too Many Requests | API를 너무 빠른 빈도로 요청했을 때 발생 |
500, 502, 503, 504 | Server Errors | CHAI API 서버에 오류 발생 |
에러 종류
Error Type | Description |
---|---|
API_CONNECTION_ERROR | CHAI API 서버에 접속할 수 없을 때 발생하는 오류 |
API_ERROR | 다른 오류 외의 경우에 발생하는 오류 (예: 일시적인 오류 발생) |
AUTHENTICATION_ERROR | 인증 실패 |
PAYMENT_ERROR | 결제 오류 (유저 취소, 잔액 부족 등) |
IDEMPOTENCY_ERROR | Idempotency Key는 같은데 다른 결제 정보가 들어올 경우 발생 |
INVALID_REQUEST_ERROR | 잘못된 파라미터를 입력하면 발생 |
RATE_LIMIT_ERROR | API를 너무 빠르게 호출할 때 발생 |
VALIDATION_ERROR | 사용자가 잘못되거나 부족한 정보를 입력했을 때 발생 |
APP_VERSION_IS_TOO_OLD | 앱 버전이 최소 지원 버전보다 낮을 때 발생 |
에러 코드
5XX Server Error
Type | Code | Message | Description |
---|---|---|---|
API_ERROR | S001 | 결제 데이터 생성 중 문제가 발생했습니다 | Transaction 실패 |
API_ERROR | S002 | 토큰을 가져오지 못했습니다 | token을 가져오지 못함 |
API_ERROR | S003 | 핀번호 검증 중 문제가 발생했습니다 | 핀번호 체크 실패 |
API_ERROR | S005 | ARS 인증에 실패했습니다 | ARS 인증 요청(승인) 실패 |
API_ERROR | S101 | 가맹점을 찾을 수 없습니다 | Merchant를 찾을 수 없음 |
API_ERROR | S102 | 계좌를 찾을 수 없습니다 | 계좌를 찾을 수 없음 |
API_ERROR | S103 | 원장을 찾을 수 없습니다 | Ledger를 찾을 수 없음 |
API_ERROR | S104 | 충전 금액을 찾을 수 없습니다 | 최초 결제시 충전금액을 찾을 수 없음 |
API_ERROR | S105 | PG사 식별자를 찾을 수 없습니다 | PG사의 취소 번호를 찾을 수 없음 |
API_ERROR | S106 | 쿠폰을 찾을 수 없습니다 | 쿠폰을 찾을 수 없음 |
API_ERROR | S107 | 은행 데이터를 찾을 수 없습니다 | 은행 목록을 불러오지 못함 |
API_ERROR | P101 | 할인 금액 계산 중 문제가 발생했습니다 | 할인금액 계산 오류 |
API_ERROR | P102 | 실 취소 금액 계산 중 문제가 발생했습니다 | 취소된 실결제금액 계산 오류 |
API_ERROR | S201 | 오류가 발생했습니다 | API Call Error |
API_ERROR | S901 | 데이터베이스 통신 중 문제가 발생했습니다 | DB Error |
API_ERROR | S902 | 처리 중 타임아웃이 발생했습니다 | Timeout |
API_ERROR | S903 | 모듈에 문제가 생겼습니다 | Module Error |
4XX Client Error
Type | Code | Message | Description |
---|---|---|---|
AUTHENTICATION_ERROR | A001 | 권한이 없습니다 | 권한 없음 |
AUTHENTICATION_ERROR | A002 | 사용자를 찾을 수 없습니다 | User를 찾을 수 없음 |
AUTHENTICATION_ERROR | A003 | 사용이 제한된 상태입니다. 해제 후 다시 시도해주세요 | Inactive User |
AUTHENTICATION_ERROR | A004 | 인증번호 확인 후 다시 입력해주세요 | 잘못된 코드 번호 |
AUTHENTICATION_ERROR | A005 | 비밀번호가 맞지 않습니다(N회 틀림) 5회 연속 틀릴 경우 사용이 정지됩니다 | 잘못된 핀번호 |
AUTHENTICATION_ERROR | A006 | 인증에 문제가 생겼습니다. 잠시 후 다시 시도해주세요 | OTP Error |
AUTHENTICATION_ERROR | A007 | 입력 시간이 만료되었습니다. 재요청을 눌러 다시 인증해주세요 | 만료됨 |
AUTHENTICATION_ERROR | A008 | 본인 인증에 실패했습니다. 입력하신 정보가 맞는지 확인해주세요 | 인증 실패 |
AUTHENTICATION_ERROR | A009 | 전화번호, 생년월일과 연속된 숫자 또는 이전 비밀번호로 설정할 수 없습니다 | 사용할 수 없는 핀번호 |
AUTHENTICATION_ERROR | A010 | 탈퇴 후 재가입은 12시간 이후에 가능합니다 | 탈퇴 후 재가입 시간 제한 |
AUTHENTICATION_ERROR | A011 | 만 14세 이하 고객은 사용하실 수 없습니다 | 사용자 나이 제한 |
AUTHENTICATION_ERROR | A012 | 결제 가능 시간이 지났습니다. 결제를 다시 시도해주세요 | 결제 상태 오류 |
AUTHENTICATION_ERROR | A013 | 인증 시간이 만료되었습니다. 본인인증부터 다시 시작해주세요 | 인증 시간 만료 |
AUTHENTICATION_ERROR | A014 | 등록된 비밀번호와 다릅니다 | 비밀번호 오류 |
AUTHENTICATION_ERROR | A019 | 유효하지 않은 SUBSCRIPTION ID입니다 | 해당 자동결제 등록건에 등록된 유저를 찾을수 없습니다 |
IDEMPOTENCY_ERROR | C001 | 결제 정보를 찾을 수 없습니다 | 결제 정보 오류 |
IDEMPOTENCY_ERROR | C002 | 가맹점 정보에 문제가 있습니다 | 잘못된 가맹점 |
IDEMPOTENCY_ERROR | C003 | 결제 상태에 문제가 있습니다 | 결제 상태 오류 |
IDEMPOTENCY_ERROR | C004 | 이미 승인 완료된 거래입니다 | 승인 완료된 거래 |
IDEMPOTENCY_ERROR | C005 | 이미 취소된 거래입니다 | 취소된 거래 |
IDEMPOTENCY_ERROR | C006 | 자동결제 상태에 문제가 있습니다 | 자동결제 등록 상태 오류 |
IDEMPOTENCY_ERROR | C007 | 자동결제 등록 정보를 찾을수 없습니다 | 자동결제 정보 오류 |
INVALID_REQUEST_ERROR | R001 | 요청이 없습니다 | 요청 없음 |
INVALID_REQUEST_ERROR | R003 | 결제 금액에 문제가 있습니다 | 요청 금액 오류 |
INVALID_REQUEST_ERROR | R004 | 화폐 단위에 문제가 있습니다 | 잘못된 화폐 |
INVALID_REQUEST_ERROR | R006 | 쿠폰 적용에 문제가 생겼습니다. 확인 후 다시 시도해주세요 | 쿠폰 오류 |
INVALID_REQUEST_ERROR | R007 | 이미 등록된 계좌입니다 | 이미 등록된 계좌 |
INVALID_REQUEST_ERROR | R008 | 계좌에 문제가 있습니다. 확인 후 다시 시도해주세요 | 계좌 오류 |
INVALID_REQUEST_ERROR | R009 | 본인 명의의 계좌만 등록하실 수 있습니다 | 본인 명의 계좌 아님 |
INVALID_REQUEST_ERROR | R010 | 은행마다 하나의 계좌만 등록 가능합니다 | 이미 존재하는 계좌 |
INVALID_REQUEST_ERROR | R011 | 계좌는 4개까지만 등록하실 수 있습니다. 기존 계좌 삭제 후 등록해주세요 | 등록 가능한 계좌 수 초과 |
INVALID_REQUEST_ERROR | R012 | 최소 1개의 계좌가 등록되어 있어야 합니다 | 최소 1개 이상의 계좌 필요 |
INVALID_REQUEST_ERROR | R013 | 취소 금액은 할인 금액보다 커야합니다 | 취소 요청 금액 오류 |
INVALID_REQUEST_ERROR | R014 | 실결제금액 이상 취소할 수 없습니다 | 실결제금액 이상 취소 불가 |
INVALID_REQUEST_ERROR | R015 | 잘못된 API 접근으로 문제가 생겼습니다 | API Key 오류 |
PAYMENT_ERROR | P001 | 이용할수 없는 계좌입니다. 은행으로 문의해주세요 | 이용할 수 없는 계좌 |
PAYMENT_ERROR | P002 | 거래정지계좌는 사용할수 없습니다. 은행으로 문의해주세요 | 거래 정지 계좌 |
PAYMENT_ERROR | P003 | 존재하지 않는 계좌입니다. 은행으로 문의해주세요 | 존재하지 않는 계좌 |
PAYMENT_ERROR | P004 | 잔액이 부족합니다. 계좌를 확인해주세요 | 잔액 부족 |
PAYMENT_ERROR | P005 | 이용한도를 초과했습니다. 은행으로 문의해주세요 | 이용 한도 초과 |
PAYMENT_ERROR | P006 | 가상계좌 입금시간이 아닙니다. 확인후 다시 시도해주세요 | 가상 계좌 입금 시간 아님 |
PAYMENT_ERROR | P007 | 은행 업무 시간이 아닙니다 | 은행 업무 시간 아님 |
PAYMENT_ERROR | P008 | 은행 점검 시간입니다. 잠시 후 시도해주세요 | 은행 점검 시간 |
PAYMENT_ERROR | P009 | 거래중 오류가 발생했습니다 | 은행 거래 오류 |
PAYMENT_ERROR | P010 | XX은행 시스템이 불안정합니다. 조금 뒤에 다시 시도해주세요 | 은행 오류 |
PAYMENT_ERROR | P011 | 1회 최소 결제 금액은 XXX원 입니다 | 최소 결제 금액 오류 |
PAYMENT_ERROR | P012 | 1회 최대 결제 금액은 XXX원 입니다 | 최대 결제 금액 오류 |
PAYMENT_ERROR | P013 | 이미 사용된 쿠폰 입니다 | 쿠폰 중복 적용 |
RATE_LIMIT_ERROR | L001 | YYYY-MM-DD HH:mm:ss까지 시도하실 수 없습니다 | 요청 제한 |
영수증 페이지
영수증 페이지에서 구매 정보, 결제 정보, 판매처 정보 및 현금영수증 발행정보를 확인할 수 있습니다.
URL
https://payment.chai.finance/receipt
URL Parameters
Key | Description |
---|---|
publicAPIKey | 차이에서 제공한 공개키 |
idempotencyKey | 중복 결제 방지 식별자 |
Sample
일대사 정산
정산 파일은 매일 오전 4시에 생성됩니다. 날짜별로 정산 파일을 다운로드 받을 수 있습니다.
URL
https://settlement.chai.finance/prod/:settlementKey/:date.txt
Key | Description |
---|---|
settlementKey | 차이 어드민에서 확인 가능한 키 |
date | 정산 날짜 (YYYYMMDD) |
Result
publicAPIKey | 거래발생시간 | 결제방식 | idempotencyKey | paymentId | 결제구분 | 결제금액(절대값) | 결제생성시간 | 결과코드 | 대금 지급일 | 결제금액 | 프로모션 분담금 | 부분취소여부 | 거래아이디 | 초기 머천트 캐시백 | 머천트 분담 캐시백 | 건별 수수료 | 건별 수수료 부가세 | 비과세 금액
Key | Description |
---|---|
publicAPIKey | 차이에서 제공한 공개키 |
거래발생시간 | 트랜잭션 발생 시간 (YYYYMMDDHHmmss) |
결제방식 | AT: 펌뱅킹 |
idempotencyKey | 결제 생성시 전달한 가맹점의 주문 번호 |
paymentId | 결제 생성시 받은 paymentId |
결제구분 | P: 결제, C: 취소 |
결제금액 | 결제한 금액 (절대값) |
원거래발생시간 | 결제 생성 시간 (YYYYMMDDHHmmss) |
결과코드 | 0000: 정상 |
대금 지급일 | 정산대금 지급일 |
결제금액 | +,- 로 표기된 결제,취소금액 |
프로모션 분담금 | 정산금액에서 제외되는 프로모션 비용 분담금 |
부분취소 여부 | 부분취소 여부 true,false |
거래아이디 | 결제,취소가 발생할때마다 생성되는 고유한 아이디 |
초기 머천트 캐시백 | 처음 결제시에 지급되었던 캐시백 금액 |
머천트 분담 캐시백 | 캐시백 프로모션 비용을 분담하기로 한 금액 |
건별 수수료 | 건별 수수료 계약시 나오는 항목 입니다. |
건별 수수료 부가세 | 건별 수수료 계약시 나오는 항목 입니다. |
비과세 금액 | 결제 생성시 비과세 금액을 전달해준 값 |
결과코드표
Code | Description |
---|---|
0000 | 정상 |
2001 | 상점관리자 수동거래 |
Sample
Sample Data
885738d9-78ed-483d-a923-e8db56aa393b|20200601150507|POINT|0d9f278c-e0c0-475c-92ca-f57ecbb07ac7|5e0290b3f5fad9625be824c517a1da5c17ef10e2|P|720000|20200601150453|0000|20200615|720000|0|false|eac5cb7f-9bb3-4f45-90a5-79893299ae4d|0|0|||0
885738d9-78ed-483d-a923-e8db56aa393b|20200601175840|AT|093f8a7b-6ea5-43c5-823c-2e5dc0c91f1f|a73411512ae18978f1e0f62714745e59946941fb|P|20000|20200601175813|0000|20200615|20000|0|false|939836a1-43f3-4c7c-ba14-0fafa64eb70d|0|0|||0
885738d9-78ed-483d-a923-e8db56aa393b|20200601144843|POINT|0e5333d3-a2ba-495e-919e-49016e725fbd|4d537eef03a80287e846457539cfa9c13714d1bd|C|20000|20200529192007|2001|20200615|-20000|0|false|87eac59d-b94e-459a-91d2-5c9900163e33|0|0|||0
https://settlement.chai.finance/staging/2oHAtE/20200804.txt