본문 바로가기
IT 초보코딩의 세계/Go 언어

Go 언어 Back End 제작해보기(신용카드 결제 처리) 7장

by 조이럭키7 2023. 6. 6.
반응형

신용카드 결제 처리

스트라이프 API 의 테스트 환경을 위한 API 와 신용카드 번호

https://stripe.com/docs/testing

프론트엔드의 스트라이프 API 이용한 토큰 생성

let { token } = await this.props.stripe.createToken({ name: this.state.name });

신용카드 결제를 처리하는데 필요한 정보

  • 스트라이프 API가 제공하는 신용카드 토큰
  • 주문자의 사용자 ID
  • 주문하는 상품의 ID
  • 상품판매가격
  • 카드를 나중에 다시 사용할 수 있도록 해당 정보 저장 여부
  • 미리 저장된 카드 사용 여부

FrontEnd 프로젝트의 creditcard.js 파일의 메서드 수정

async handleSubmit(event) {

        event.preventDefault();
        let id = "";
        if (!this.state.useExisting) {
            let { token } = await this.props.stripe.createToken({ name: this.state.name });
            if (token == null) {
                console.log("invalid token");
                this.setState({ status: FAILEDSTATE });
                return;
            }
            id = token.id;
        }

        let response = await fetch("/users/charge", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                token: id,
                customer_id: this.props.user,
                product_id: this.props.productid,
                sell_price: this.props.price,
                rememberCard: this.state.remember !== undefined,
                useExisting: this.state.useExisting
            })
        });

        // let response = await this.sendChargeRequest(token,false);
        if (response.ok) {
            console.log("Purchase Complete!");
            this.setState({ status: SUCCESSSTATE });
        } else {
            this.setState({ status: FAILEDSTATE });
        }
    }

BackEnd 에서 신용카드 결제 요청 처리

순서

  • 프론트엔드가 보낸 토큰을 처리하는 *stripe.CustomerParams 타입 인스턴스를 생성하는데 SetToken() 메서드를 사용해 토큰을 설정
  • stripe.CustomerParams 타입을 전달받는 *stripe.Customer 타입 인스턴스를 생성하는데  customer.New() 함수를 사용
  • 수량, 통화 등의 결제 관련 정보를 저장하는 *stripe.ChargeParams 타입 인스턴스를 생성
  • *stripe.ChargeParams 타입은 스트라이프 사용자 ID 없이 생성할 수 없는데 이 값은 2단계의 *stripe.Customer 인스턴스에 저장돼 있음
  • 해당 신용카드 정보를 다음에 다시 사용하고 싶다면 스트라이프 사용자 ID 저장
  • *stripe.ChargeParams 타입 매개변수를 받는 charge.New() 메서드를 호출하고 결제를 요청

handler.go 파일의 Charge 메서드 수정

func (h *Handler) Charge(c *gin.Context) {
  if h.db == nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": "server database error"})
  return
  }

  //프론트엔드에서 전달받은 구조체를 정의
  request := struct {
  models.Order
  Remember    bool   `json:"rememberCard"`
  UseExisting bool   `json:"useExisting"`
  Token       string `json:"token"`
  }{}

  //JSON 형식의 요청 데이터를 request 구조체로 변환
  err := c.ShouldBindJSON(&request)
  log.Printf("request: %+v \n", request)
  if err != nil {
  c.JSON(http.StatusBadRequest, request)
  return
  }
  stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"

  //stripe.ChargeParams 타입 인스턴스를 생성
  chargeP := &stripe.ChargeParams{
  Amount:      stripe.Int64(int64(request.Price)),
  Currency:    stripe.String("usd"),
  Description: stripe.String("GoMusic charge..."),
  }

  //스트라이프 사용자 ID를 초기화
  stripeCustomerID := ""
  //이미지 저장해 둔 신용카드로 결제하는 경우라면 데이터베이스에서 스트라이프 사용자 ID를 조회
  if request.UseExisting {
  //저장된 카드 사용
  log.Println("Getting credit card id...")
  stripeCustomerID, err = h.db.GetCreditCardCID(request.CustomerID)
  if err != nil {
  log.Println(err)
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  return
  }

  } else {
  cp := &stripe.CustomerParams{}
  cp.SetSource(request.Token)
  customer, err := customer.New(cp)
  if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  return
  }
  stripeCustomerID = customer.ID
  //스트라이프 사용자 ID를 저장하고 데이터베이스에 저장된 사용자 ID 와 연결
  if request.Remember {
  //save card!!
  err = h.db.SaveCreditCardForCustomer(request.CustomerID, stripeCustomerID)
  if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  return
  }
  }
  }
  //결제 요청
  chargeP.Customer = stripe.String(stripeCustomerID)
  //신용카드 결제 요청
  _, err = charge.New(chargeP)
  if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  return
  }

  //데이터베이스에 주문 내용을 저장
  err = h.db.AddOrder(request.Order)
  if err != nil {
  c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
  }
}

 

dblayer.go 파일의 인터페이스에 메서드 추가

type DBLayer interface {
  GetAllProducts() ([]models.Product, error)
  GetPromos() ([]models.Product, error)
  GetCustomerByName(string, string) (models.Customer, error)
  GetCustomerByID(int) (models.Customer, error)
  GetProduct(int) (models.Product, error)
  AddUser(models.Customer) (models.Customer, error)
  SignInUser(username, password string) (models.Customer, error)
  SignOutUserById(int) error
  GetCustomerOrdersByID(int) ([]models.Order, error)
  AddOrder(models.Order) error
  GetCreditCardCID(int) (string, error)
  SaveCreditCardForCustomer(int, string) error
}

orm.go 파일에서 메서드 구현

func (db *DBORM) AddOrder(order models.Order) error {

  return db.Create(&order).Error
}

func (db *DBORM) GetCreditCardCID(id int) (string, error) {

  cusomterWithCCID := struct {
  models.Customer
  CCID string `gorm:"column:cc_customerid"`
  }{}

  return cusomterWithCCID.CCID, db.First(&cusomterWithCCID, id).Error
}

func (db *DBORM) SaveCreditCardForCustomer(id int, ccid string) error {
  result := db.Table("customers").Where("id=?", id)
  return result.Update("cc_customerid", ccid).Error
}

handler.go 파일의 NewHandler 파일 수정

func NewHandler(db, constring string) (HandlerInterface, error) {
  db, err := dblayer.NewORM(db, constring)
  if err != nil {
  return nil, err
  }
  return &Handler{
  db: db,
  }, nil
}
반응형

댓글