◆ 상품 주문 모달 윈도
◆ 모달을 위한 패키지 설치: npm install -–save reactstrap
◆ 상품 주문 모달 윈도 – modalwindow.js 파일을 src 디렉토리에 생성하고 작성
export function BuyModalWindow(props) {
return (
<Modal id="buy" tabIndex="-1" role="dialog" isOpen={props.showModal} toggle={props.toggle}>
<div role="document">
<ModalHeader toggle={props.toggle} className="bg-success text-white">
Buy Item
</ModalHeader>
<ModalBody>
<CreditCardInformation user={props.user} seperator={false} show={true} productid={props.productid} price={props.price} operation="Charge" toggle={props.toggle} />
</ModalBody>
</div>
</Modal>
);
}
◆ 신용카드 결제 처리
▶ 신용카드 정보 입력 폼 구현은 간단해 보일 수 있지만 텍스트 입력 창이 전부가 아니며 실제 운영 중인 서비스라면 신용카드 유효성을 검사하고 입력받은 정보를 안전하게 처리해야 하는데 신용카드 정보는 매우 민감한 정보이기 때문에 일반 데이터처럼 취급할 수 없음
▶ 프론트 엔드에서 사용할 수 있는 다양한 신용카드 결제 서비스가 있는데 그 중 가장 많이 쓰이는 스트라이프 (https://stripe.com/)를 사용
▶ 다른 유사한 서비스와 마찬가지로 웹 사이트에 접속해서 스트라이프 계정을 생성하고 API 키를 발급 받아야 하며 이 단계에서 결제액을 입금받을 은행 계좌도 입력해야 함
▶ 개발과 테스트 용도로 제공하는 테스트 API를 사용
▶ 스트라이프는 애플리케이션 내의 신용카드 결제에 필요한 모든 단계를 지원
▶ 신용카드 번호의 유효성을 검사하고 입력된 승인 금액으로 결제를 요청한 후 최종적으로 결제액을 계좌로 입금
▶ 리액트 스트라이프 엘리먼트: https://github.com/stripe-archive/react-stripe-elements
● UI 엘리먼트는 신용카드 번호와 유효기간, CVC 번호, 우편번호 등의 신용카드 정보를 입력받음
● 입력된 카드번호가 Master 또는 Visa인지 확인하는 등의 추가 데이터를 검증
● 입력된 데이터를 검증하고 신용카드 정보를 나타내는 토큰 ID 값을 발급받고 해당 ID를 백엔드에 저장하고 사용
▶ 신용카드 결제 폼 작성 순서
● 신용카드 결제 폼을 감싸는 컴포넌트를 생성 - 이 컴포넌트의 이름은 child
● child 컴포넌트 안에 신용카드 정보를 입력하는 필드들을 포함하는 스트라이프 엘리먼트를 추가하는데 각 필드는 신용카드 번호와 유효기간 등을 입력 받는 일반적인 텍스트 입력 창
● child 컴포넌트에 신용카드 토큰 ID를 백엔드로 전송하는 코드를 추가
● 스트라이프 엘리먼트를 감싸는 컴포넌트의 부모 클래스를 정의
→ 스트라이프 API 키를 처리하는 StripeProvider 컴포넌트를 호스트
→ StripeProvider 컴포넌트 안에 child 컴포넌트를 추가
→ 스트라이프 props와 함수를 child 컴포넌트에 전달하는데 injectStripe 메서드를 사용
▶ 패키지 설치
● npm install @stripe/react-stripe-js @stripe/stripe-js
● npm install --save react-stripe-elements
▶ 신용카드 결제 처리 폼을 작성 – src/CreditCards.js 파일
import React from 'react';
import { injectStripe, StripeProvider, Elements, CardElement } from 'react-stripe-elements';
const INITIALSTATE = "INITIAL", SUCCESSSTATE = "COMPLETE", FAILEDSTATE = "FAILED";
class CreditCardForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.state = {
value: '',
status: INITIALSTATE
};
}
renderCreditCardInformation() {
const style = {
base: {
'fontSize': '20px',
'color': '#495057',
'fontFamily': 'apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif'
}
};
const usersavedcard = <div>
<div className="form-row text-center">
<button type="button" className="btn btn-outline-success text-center mx-auto">Use saved card?</button>
</div>
<hr />
</div>
const remembercardcheck = <div className="form-row form-check text-center">
<input className="form-check-input" type="checkbox" value="" id="remembercardcheck" />
<label className="form-check-label" htmlFor="remembercardcheck">
Remember Card?
</label>
</div>;
return (
<div>
{usersavedcard}
<h5 className="mb-4">Payment Info</h5>
<form onSubmit={this.handleSubmit}>
<div className="form-row">
<div className="col-lg-12 form-group">
<label htmlFor="cc-name">Name On Card:</label>
<input id="cc-name" name='cc-name' className="form-control" placeholder='Name on Card' onChange={this.handleInputChange} type='text' />
</div>
</div>
<div className="form-row">
<div className="col-lg-12 form-group">
<label htmlFor="card">Card Information:</label>
<CardElement id="card" className="form-control" style={style} />
</div>
</div>
{remembercardcheck}
<hr className="mb-4" />
<button type="submit" className="btn btn-success btn-large" >{this.props.operation}</button>
</form>
</div>
);
}
return (
<div>
<h5 className="mb-4 text-success">Credit card payment processed.....</h5>
<button type="submit" className="btn btn-success btn-large" onClick={() => { this.props.toggle() }}>Ok</button>
</div>
);
}
renderFailure() {
return (
<div>
<h5 className="mb-4 text-danger"> Credit card information invalid, try again or exit</h5>
{this.renderCreditCardInformation()}
</div>
);
}
async handleSubmit(event) {
event.preventDefault();
console.log("Handle submit called, with name: " + this.state.value);
let { token } = await this.props.stripe.createToken({ name: this.state.value });
if (token == null) {
console.log("invalid token");
this.setState({ status: FAILEDSTATE });
return;
}
let response = await fetch("/charge", {
method: "POST",
headers: { "Content-Type": "text/plain" },
body: JSON.stringify({
token: token.id,
operation: this.props.operation,
})
});
console.log(response.ok);
if (response.ok) {
console.log("Purchase Complete!");
this.setState({ status: SUCCESSSTATE });
}
// document.getElementsByClassName('#modal').modal('hide');
}
handleInputChange(event) {
this.setState({
value: event.target.value
});
}
render() {
let body = null;
switch (this.state.status) {
case SUCCESSSTATE:
body = this.renderSuccess();
break;
case FAILEDSTATE:
body = this.renderFailure();
break;
default:
body = this.renderCreditCardInformation();
}
return (
<div>
{body}
</div>
);
}
}
export default function CreditCardInformation(props) {
if (!props.show) {
return null;
}
const CCFormWithStripe = injectStripe(CreditCardForm);
return (
<div>
{props.separator ? <hr /> : null}
<StripeProvider apiKey="pk_test_LwL4RUtinpP3PXzYirX2jNfR">
<Elements>
<CCFormWithStripe operation={props.operation} />
</Elements>
</StripeProvider>
</div>
);
}
'IT 초보코딩의 세계 > Go 언어' 카테고리의 다른 글
Go 언어 Front End 제작해보기(로그인 페이지) 4장 (6) | 2023.05.12 |
---|---|
Go 언어 Front End 제작해보기(회원 가입) 3장 (4) | 2023.05.11 |
Go 언어 Front End 제작해보기 1장 (16) | 2023.05.05 |
[Block Chain] Go언어의 RESTful API, Gin Framework, Model&Database Layer 1장 (12) | 2023.04.15 |
[Block Chain] Go언어의 Web Download 3장 (0) | 2023.04.14 |
댓글