-
Go: fiber v2 백엔드에서 토스 페이 API 사용하기컴퓨터/Go language 2024. 3. 4. 12:20728x90반응형
할 것
Go언어 백엔드 서버에 toss payments API를 연동해보고 싶었다.
우선 API 키를 얻어준다. @링크
테스트 키로 하면 아무리 결제해도 실제 돈은 빠지지 않는다.
이해하기
우선 토스 페이먼츠는 Basic 인증 방식을 사용하고 (Authorization: Basic blah 헤더)
시크릿 키가 ID고 비밀번호는 없는 형태고, 모든 응답/요청은 JSON 이다.
위 링크에 들어가서 react와 pure js 버전을 참고하면 좋다.
처음에 백엔드에서 뭘 하고, 프론트에서 뭘 해야하는지 살짝 헷갈렸다.
/confirm, /success, /fail 이 있는데 백엔드에서 다 처리해야 하나?
물론 케바케지만 샌드박스 형식을 이해한대로 하면
1. frontend 에서 토스를 부르고 (ck 가 있는 customer key를 이용)
2. frontend에서 결제를 눌러 결제 요청이
2.1 성공하면) frontend/success 처럼 router를 만들어서 그 페이지에서 backend/confirm 엔드포인트를 POST 하게 한다. (바로 넘어가던가, 결제 요청 완료 했으니 결제 승인하기! 버튼을 만들어서 넘어가기)
2.2 실패하면) frontend/fail router를 만들어서 알아서 처리 (물론 에러 코드/메시지는 있다)
Go언어
fiber v2 + golang v1.22.0 기준이다.
Confirm endpoint
.env 에 TOSS_SECRET_KEY_TEST를 만들어서 API 값을 넣어줬다.
var ( TOSS_SECRET_KEY = "Basic " + base64.StdEncoding.EncodeToString([]byte(os.Getenv("TOSS_SECRET_KEY_TEST")+":")) TOSS_CONFIRM_API_URL = "https://api.tosspayments.com/v1/payments/confirm" TOSS_PAYMENT_API_URL = "https://api.tosspayments.com/v1/payments/" ) func ConfirmToss(c *fiber.Ctx) error { // Extract paymentKey, orderId, and amount from the request body var requestBody struct { PaymentKey string `json:"paymentKey"` OrderId string `json:"orderId"` Amount int `json:"amount"` } if err := c.BodyParser(&requestBody); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "error": "Invalid request body", }) } // POST agent agent := fiber.Post(TOSS_CONFIRM_API_URL) agent.Set("Authorization", TOSS_SECRET_KEY) agent.ContentType("application/json") agent.Body(c.Body()) // json: {orderId: "aaa", amount: ?, paymentKey: "bbb"} // 요청 발생함 statusCode, body, errs := agent.Bytes() if len(errs) > 0 { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "errs": errs, }) } return c.Status(statusCode).Send(body) }
그리고 샌드박스 사이트에서 내 백엔드 confirm 링크로 대체해서 테스트 해보니 성공을 한다.
optional) 결제에 관한 모든 정보를 확인하고 싶으면 "https://api.tosspayments.com/v1/payments/"
에 paymentKey나 orderId를 url에 넣어서 GET 하면 된다. 객체는 다음과 같이 생김
type Payment struct { Version string `json:"version"` PaymentKey string `json:"paymentKey"` Type string `json:"type"` OrderId string `json:"orderId"` OrderName string `json:"orderName"` MId string `json:"mId"` Currency string `json:"currency"` Method string `json:"method"` TotalAmount float64 `json:"totalAmount"` BalanceAmount float64 `json:"balanceAmount"` Status string `json:"status"` RequestedAt string `json:"requestedAt"` ApprovedAt string `json:"approvedAt"` UseEscrow bool `json:"useEscrow"` LastTransactionKey *string `json:"lastTransactionKey,omitempty"` SuppliedAmount float64 `json:"suppliedAmount"` Vat float64 `json:"vat"` CultureExpense bool `json:"cultureExpense"` TaxFreeAmount float64 `json:"taxFreeAmount"` TaxExemptionAmount int `json:"taxExemptionAmount"` Cancels []Cancel `json:"cancels,omitempty"` IsPartialCancelable bool `json:"isPartialCancelable"` Card *CardInfo `json:"card,omitempty"` VirtualAccount *VirtualAccountInfo `json:"virtualAccount,omitempty"` MobilePhone *MobilePhoneInfo `json:"mobilePhone,omitempty"` GiftCertificate *GiftCertificateInfo `json:"giftCertificate,omitempty"` Transfer *TransferInfo `json:"transfer,omitempty"` Receipt *ReceiptInfo `json:"receipt,omitempty"` Checkout *CheckoutInfo `json:"checkout,omitempty"` EasyPay *EasyPayInfo `json:"easyPay,omitempty"` Country string `json:"country,omitempty"` Failure *FailureInfo `json:"failure,omitempty"` CashReceipt *CashReceiptInfo `json:"cashReceipt,omitempty"` CashReceipts []CashReceiptInfo `json:"cashReceipts,omitempty"` Discount *DiscountInfo `json:"discount,omitempty"` } type CardInfo struct { Amount float64 `json:"amount"` IssuerCode string `json:"issuerCode"` AcquirerCode *string `json:"acquirerCode,omitempty"` Number string `json:"number"` InstallmentPlanMonths int `json:"installmentPlanMonths"` ApproveNo string `json:"approveNo"` UseCardPoint bool `json:"useCardPoint"` CardType string `json:"cardType"` OwnerType string `json:"ownerType"` AcquireStatus string `json:"acquireStatus"` IsInterestFree bool `json:"isInterestFree"` InterestPayer *string `json:"interestPayer,omitempty"` } type VirtualAccountInfo struct { AccountType string `json:"accountType"` AccountNumber string `json:"accountNumber"` BankCode string `json:"bankCode"` CustomerName string `json:"customerName"` DueDate string `json:"dueDate"` RefundStatus string `json:"refundStatus"` Expired bool `json:"expired"` SettlementStatus string `json:"settlementStatus"` RefundReceiveAccount *RefundReceiveAccountInfo `json:"refundReceiveAccount,omitempty"` Secret *string `json:"secret,omitempty"` } type MobilePhoneInfo struct { CustomerMobilePhone string `json:"customerMobilePhone"` SettlementStatus string `json:"settlementStatus"` ReceiptUrl string `json:"receiptUrl"` } type GiftCertificateInfo struct { ApproveNo string `json:"approveNo"` SettlementStatus string `json:"settlementStatus"` } type TransferInfo struct { BankCode string `json:"bankCode"` SettlementStatus string `json:"settlementStatus"` } type ReceiptInfo struct { Url string `json:"url"` } type CheckoutInfo struct { Url string `json:"url"` } type EasyPayInfo struct { Provider string `json:"provider"` Amount float64 `json:"amount"` DiscountAmount float64 `json:"discountAmount"` } type FailureInfo struct { Code string `json:"code"` Message string `json:"message"` } type CashReceiptInfo struct { Type string `json:"type"` ReceiptKey string `json:"receiptKey"` IssueNumber string `json:"issueNumber"` ReceiptUrl string `json:"receiptUrl"` Amount float64 `json:"amount"` TaxFreeAmount float64 `json:"taxFreeAmount"` IssueStatus string `json:"issueStatus"` Failure *FailureInfo `json:"failure,omitempty"` CustomerIdentityNumber string `json:"customerIdentityNumber"` RequestedAt string `json:"requestedAt"` } type DiscountInfo struct { Amount int `json:"amount"` } type RefundReceiveAccountInfo struct { BankCode string `json:"bankCode"` AccountNumber string `json:"accountNumber"` HolderName string `json:"holderName"` } type CashReceipt struct { ReceiptKey string `json:"receiptKey"` IssueNumber string `json:"issueNumber"` IssueStatus string `json:"issueStatus"` Amount int `json:"amount"` TaxFreeAmount int `json:"taxFreeAmount"` OrderId string `json:"orderId"` OrderName string `json:"orderName"` Type string `json:"type"` TransactionType string `json:"transactionType"` BusinessNumber string `json:"businessNumber"` CustomerIdentityNumber string `json:"customerIdentityNumber"` Failure *FailureDetail `json:"failure,omitempty"` RequestedAt string `json:"requestedAt"` ReceiptUrl string `json:"receiptUrl"` } type FailureDetail struct { Code string `json:"code"` Message string `json:"message"` } type Cancel struct { CancelAmount int `json:"cancelAmount"` CancelReason string `json:"cancelReason"` TaxFreeAmount int `json:"taxFreeAmount"` TaxExemptionAmount int `json:"taxExemptionAmount"` RefundableAmount int `json:"refundableAmount"` EasyPayDiscountAmount int `json:"easyPayDiscountAmount"` CanceledAt time.Time `json:"canceledAt"` TransactionKey string `json:"transactionKey"` ReceiptKey string `json:"receiptKey,omitempty"` } ... // handler func SuccessToss(c *fiber.Ctx) error { paymentKey := c.Query("paymentKey") // Fiber 에서 Get 요청 보내기 (fasthttp 기반 클라이언트임) agent := fiber.Get(TOSS_PAYMENT_API_URL + paymentKey) agent.Set("Authorization", TOSS_SECRET_KEY) agent.ContentType("application/json") statusCode, body, errs := agent.Bytes() if len(errs) > 0 { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "errs": errs, }) } var responseMap dto.Payment err := json.Unmarshal(body, &responseMap) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "error": err.Error(), }) } log.Printf("Code: %+v", statusCode) log.Printf("errs: %+v", errs) log.Printf("Response: %+v", responseMap) return c.Render("success", fiber.Map{ "IsSuccess": statusCode == 200, "Response": responseMap, }) }
728x90'컴퓨터 > Go language' 카테고리의 다른 글
Go: WGS84를 WCONGNAMUL로 변환 함수 (3) 2024.03.24 dismember: 메모리 탐색기 (0) 2022.07.06 Wails: Go언어를 이용해 크로스 플랫폼 앱 만들기 (0) 2022.06.26