티스토리 뷰
프로젝트 [만원의 행복] 개발 중에 이미지 저장하는 DB가 아직 구축이 되지 않아서,
일단 MySql에 이미지로 저장하고 프론트엔드한테는 endpoint api로 전달하기로 했다.
✔️ 결제 처리 로직
1) 짠처리 상품 구매
2) 서버 : 결제 API 처리
3) 서버 : QR code 발급
1) 짠처리 상품 사용하고 싶음
2) 업주 : QR code 찍음
3) 서버 : QR code 사용처리
4) 상품 제공
✔️ ERD 는 다음과 같다. (아직 수정 중이고 많이 부족하다ㅜ)
📌 QR image Entity (QR code 이미지 저장 entity)
일단 QR 코드 (이미지) 를 저장할 수 있는 entity는 다음과 같다.
이미지는 byte 타입으로 저장해야하므로, byte[] 로 선언한다.
✔️ @Lob @Column(name = "qr_image", columnDefinition = "MEDIUMBLOB")
해줘야 긴 url도 큐알로 저장이 가능함.
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class QrImage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "qr_image_id")
@Comment("QR Image id")
private Long id;
@Lob
@Column(name = "qr_image", columnDefinition = "MEDIUMBLOB")
@Comment("QR 이미지")
private byte[] qrImage;
}
📌 QR code 생성하는 방법
출처 : https://isshosng.tistory.com/131
1. 의존성
대부분의 QR 코드는 구글에서 나온 api를 사용한다.
// QR CODE 관련
implementation group: 'com.google.zxing', name: 'javase', version: '3.5.0'
implementation group: 'com.google.zxing', name: 'core', version: '3.5.0'
2. QR Util
1) QR 코드 생성할 수 있는 로직
2) QR 코드 이미지 얻을 수 있는 api -> DB에 저장
@Slf4j
public class QRCodeUtil {
/**
* 주어진 링크를 인코딩하여 QR 코드 이미지를 생성하고,
* 그 이미지를 byte 배열 형태로 반환하는 메서드
* @param link
* @return QR 코드 이미지를 바이트 배열 형태로 변환
* @throws DefaultException
*/
public static byte[] generateQRCodeImage(String link) {
try {
int width = 200, height = 200;
// QR코드 생성 옵션 설정
Map<EncodeHintType, Object> hintMap = new HashMap<>();
hintMap.put(EncodeHintType.MARGIN, 0);
hintMap.put(EncodeHintType.CHARACTER_SET,"UTF-8");
// QR 코드 생성
QRCodeWriter qrCodeWriter = new QRCodeWriter();
BitMatrix bitMatrix = qrCodeWriter.encode(link, BarcodeFormat.QR_CODE, width, height, hintMap);
// QR 코드 이미지 생성
BufferedImage qrCodeImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
// QR 코드 이미지를 바이트 배열로 변환, byteArrayOutputStream 에 저장
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(qrCodeImage,"png", byteArrayOutputStream);
byteArrayOutputStream.flush();
byte[] qrCodeBytes = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return qrCodeBytes;
} catch (Exception e) {
log.info("QRCode Error");
throw new QrCodeException(FAILED_CREATE_QR);
}
}
/**
* 큐알 코드 이미지 얻을 수 있는 api endpoint를 담은 url 반환
* @param qrImage
* @return
*/
public static String getQrImageURL(QrImage qrImage) {
return "http://localhost:8080/api/zzan-items/qr-code-images/" + qrImage.getId();
}
}
3. 결제 기록 테이블에 QR 이미지 링크 삽입
📝 PurchaseHistory : 결제 기록 테이블 -> 결제 후 QR 코드가 생성되면, 테이블에 저장
public class PurchaseHistory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "purchase_history_id")
@Comment("구매 기록 ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
@Comment("구매자")
private Member member;
@Comment("짠처리 아이템 FK")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "zzan_item_id")
private ZzanItem zzanItem;
@Comment("구매 가능한 QR 코드 id")
@JoinColumn(name = "qr_image_id")
@OneToOne
@Setter
private QrImage qrImage;
@Comment("QR 사용 여부")
@Enumerated(EnumType.STRING)
@Setter
private PurchaseStatusType status;
@Comment("구매 날짜 및 시간")
private LocalDateTime createdAt;
@Comment("QR 사용 날짜 및 시간")
@Setter
private LocalDateTime usedAt;
@Comment("구매 가격")
private Integer price;
}
📝 생성된 QR를 저장할 수 있는 로직
QR 코드 목적 : 주문 기록의 status 를 사용 완료로 표기 (카카오톡 기프티콘 같은 원리)
즉, 결제는 완료한 상품을 실제로 사용하기 위해서 필요한 QR 코드이다.
QR 코드에는 결제한 상품을 사용완료로 변경할 수 있는 api endpoint를 link로 만들어서 들어간다.
현재 배포를 하지 않은 상태이므로, 로컬로 만들어줬다.
▶️ QR에는 purchase_Id 가 필수로 들어간다.
▶️ ▶️ 그래서 purchase 객체를 미리 save()한 후에, setQrImage(qrImage)를 해서 또 save() 해줘야한다. (save 2번)
private static final String useLink = "http://localhost:8080/api/zzan-items/use/";
private PurchaseHistory saveQrImageInPurchaseHistory(PurchaseHistory ph) {
StringBuilder link = new StringBuilder(useLink).append(ph.getId());
QrImage qrImage = createQrImage(link.toString());
ph.setQrImage(qrImage);
PurchaseHistory savePh = purchaseHistoryRepository.save(ph);
if (savePh == null) {
throw new PurchaseException(PurchaseExceptionCode.FAILED_PURCHASE);
}
return savePh;
}
4. API
1) 주문 처리 api
@RestController
@RequestMapping("/api/zzan-items")
@RequiredArgsConstructor
public class PurchaseController {
private final PurchaseService purchaseService;
@GetMapping("/{zzanItemId}/purchase")
public ResponseEntity<DataBody<PurchaseResultResponse>> purchase(
@PathVariable final Long zzanItemId
) {
return ResponseDTO.created(
purchaseService.purchase(zzanItemId),
"주문 처리 완료"
);
}
2) QR 이미지 반환 api
[GET] 방식으로 해줘야한다.
/** QR code Image 반환 **/
@GetMapping("/qr-code-images/{qrId}")
public ResponseEntity<byte[]> getQRCodeImage(@PathVariable Long qrId) {
// 이미지 데이터와 헤더 같이 반환
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return ResponseEntity.ok().headers(headers).body(
purchaseService.getQRCodeImage(qrId)
);
}
▶️ 서비스에서는
@Transactional(readOnly = true)
public byte[] getQRCodeImage(Long qrId) {
return qrImageRepository.findById(qrId)
.orElseThrow(() -> new QrCodeException(NOT_FOUND_QRCODE_IMAGE))
.getQrImage();
}
3) QR 코드의 링크 들어갔을 때 api
[GET] 방식으로 해줘야 한다.
@GetMapping("/use/{purchaseId}")
public ResponseEntity<DataBody<Void>> useQrCode(
@PathVariable final Long purchaseId
) {
purchaseService.usePurchase(purchaseId);
return ResponseDTO.ok("QR 사용 완료");
}
🌟 실행 결과
1. zzan-item 의 139번 id를 가진 상품을 결제하면, 다음과 같은 응답을 받는다.
usedTime : 사용 완료 시점 (결제 직후 이므로, null 로 반환)
2. 빨간 박스 안에 있는 이미지를 클릭하면, 다음과 같은 QR 이미지를 받는다.
3. 해당 큐알을 카메라로 인식하고, url를 타고 들어가면 다음과 같은 응답을 받고, 결과가 갱신된다.
4. 데이터베이스에서는 status 가 To Be Use -> In Use 로 변경된다!
'Spring boot' 카테고리의 다른 글
[Spring boot] EC2 배포시 spring boot Timezone 한국 시간으로 변경하기 (0) | 2024.01.18 |
---|---|
[문제 해결] [성능 개선] redis를 이용한 주문 재고 관리 동시성 제어 (Facade 패턴, AOP 이용) (1) | 2023.12.20 |
[성능 개선] 상품 주문 시, 비관적 Lock으로 동시성 제어하기 : curl을 이용한 동시성 테스트 (0) | 2023.11.30 |
[성능 개선] 비동기 방식, saveAll을 활용한 2초 ▶️ 0.2초로 개선하기 (1) | 2023.11.17 |
[리팩토링 & 예외처리] 인터페이스를 이용한 enum으로 예외처리 정돈하기. (0) | 2023.11.15 |
- Total
- Today
- Yesterday
- 백엔드부트캠프
- 야놀자X패스트캠퍼스부트캠프
- 자료구조 #스택 #큐 #덱 #선형자료구조
- 패스트캠퍼스
- 백준
- 백엔드개발자
- 채팅기능개발
- be
- 패스트캠퍼스강의
- Java
- 백엔드
- 국비지원캠프
- 그룹스터디워크샵
- 국비지원
- 과정중간회고
- 부트캠프
- 데이터베이스
- qjzl
- 카카오API
- boj
- 그룹스터디
- TiL
- 스터디후기
- 자료구조
- springboot
- 국비지원취업
- 야놀자
- 프로젝트후기
- #국비지원취업
- 커리어멘토링
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |