백로그
0할 일
0진행 중
0리뷰
0완료 (30일)
3CustomPaymentLink 모델 + 마이그레이션 + 라우팅 + 서비스
## 작업 내용 CustomPaymentLink 모델, DB 마이그레이션, 라우팅, 서비스 객체를 생성합니다. ## 1. DB 마이그레이션 ```ruby create_table :custom_payment_links, id: :uuid do |t| t.string :title, null: false # 상품명 t.integer :amount, null: false # 결제 금액 (원) t.text :description # 상세 설명 t.string :status, default: "pending", null: false # pending/completed/canceled t.string :token, null: false # URL 토큰 t.datetime :expires_at, null: false # 만료일 t.references :created_by, type: :uuid, null: false, foreign_key: { to_table: :users } t.references :payment, type: :uuid, foreign_key: true # 결제 완료 시 연결 t.datetime :paid_at # 결제 완료 시각 t.timestamps end add_index :custom_payment_links, :token, unique: true add_index :custom_payment_links, :status ``` ## 2. CustomPaymentLink 모델 - belongs_to :created_by, class_name: "User" - belongs_to :payment, optional: true - enum :status (pending, completed, canceled) - validates :title, :amount, :token, :expires_at presence - validates :amount, numericality: { greater_than: 0 } - validates :token, uniqueness: true - before_validation :generate_token (SecureRandom.urlsafe_base64(32)) - scope :active → pending + 만료 안 됨 - scope :recent → created_at desc - expired? 메서드 - complete!(payment:) 메서드 - cancel! 메서드 ## 3. 라우팅 (config/routes.rb) admin namespace 안에 추가: ```ruby resources :custom_payment_links, only: %i[index new create] do member do patch :cancel get :qr_code end end ``` locale scope 밖에 추가 (토큰 기반 공개 접근): ```ruby # Custom Payment Links (토큰 기반 공개 결제) get "pay/:token", to: "custom_payments#show", as: :custom_payment get "pay/:token/success", to: "custom_payments#success", as: :custom_payment_success get "pay/:token/fail", to: "custom_payments#fail", as: :custom_payment_fail ``` ## 4. 서비스 객체 ### CustomPaymentLinks::CreateService - 입력: title, amount, description, expires_in(일수), created_by(User) - expires_at = expires_in.days.from_now - CustomPaymentLink.create! - 반환: custom_payment_link ### CustomPaymentLinks::CancelService - 입력: custom_payment_link - pending? 검증 - update!(status: "canceled") ## 5. 테스트 - 모델 테스트: 유효성 검증, 상태 전환, scope - 서비스 테스트: 생성, 취소 시나리오 ## 담당 파일 - db/migrate/XXXX_create_custom_payment_links.rb - app/models/custom_payment_link.rb - config/routes.rb - app/services/custom_payment_links/create_service.rb - app/services/custom_payment_links/cancel_service.rb - test/models/custom_payment_link_test.rb - test/fixtures/custom_payment_links.yml ## 완료 기준 - 마이그레이션 실행 성공 - 모델 테스트 통과 - 서비스 테스트 통과 - routes 정상 작동
관리자 커스텀 결제 링크 UI (컨트롤러 + 뷰 + 사이드바)
## 작업 내용 관리자가 커스텀 결제 링크를 생성하고 관리할 수 있는 Admin UI를 구현합니다. ## 1. Admin::CustomPaymentLinksController - BaseController 상속 (Admin::BaseController) - index: 결제 링크 목록 (최신순, 페이지네이션 없이 전체) - new: 생성 폼 - create: CustomPaymentLinks::CreateService 호출 - cancel: CustomPaymentLinks::CancelService 호출 - qr_code: QR 코드 SVG 반환 ## 2. 뷰 (Admin Views) ### index.html.erb - 결제 링크 목록 참고 이미지의 UI를 구현: - 헤더: "생성된 결제 링크 목록", "총 N개의 커스텀 결제 링크" - 테이블 컬럼: 상품명, 금액, 상태, 생성일, 만료일, 작업 - 상태 배지: - 대기중 (pending): 회색 배경, "⏳ 대기중" - 결제완료 (completed): 보라색 배경, "✅ 결제완료" - 취소됨 (canceled): 회색 텍스트, "⊘ 취소됨" - 작업 컬럼: - 대기중: "복사" 버튼 + "취소" 버튼 (빨간색) - 결제완료: "결제완료: YYYY.MM.DD" 텍스트 - 취소됨: 작업 없음 - "새 결제 링크 만들기" 버튼 (상단) - 금액은 number_to_currency(amount, unit: "₩", precision: 0) 형식 ### new.html.erb - 결제 링크 생성 폼 참고 이미지의 UI를 구현: - 제목: "커스텀 결제 링크 생성" - 설명: "관리자가 임의의 금액으로 결제 링크를 생성할 수 있습니다. 생성된 링크는 고객에게 공유하여 결제를 받을 수 있습니다." - 폼 필드: - 상품명 * (text_field, placeholder: "예: 1:1 코칭 컨설팅") - 결제 금액 (원) * (number_field, placeholder: "예: 150000") - 상세 설명 (선택) (text_area, placeholder: "예: 2시간 1:1 강점 코칭 세션") - 유효 기간 (select: 7일/14일/30일) - "결제 링크 생성" 버튼 (보라색, 전체 너비) ### QR 코드 기능 - rqrcode gem 사용 (이미 Gemfile에 있음) - 링크 목록에서 "QR" 아이콘 클릭 시 모달/팝오버로 QR 표시 - Stimulus controller로 모달 토글 ### 링크 복사 기능 - "복사" 버튼 클릭 시 클립보드에 결제 링크 URL 복사 - navigator.clipboard.writeText() 사용 - Stimulus controller: clipboard_controller.js ## 3. 사이드바 수정 app/views/admin/shared/_sidebar.html.erb에 추가: - workspaces 메뉴 아래에 추가 - icon: "payments" (결제 아이콘 SVG path 추가) - label: "커스텀 결제" - active: controller_name == "custom_payment_links" - _nav_item.html.erb의 icons 해시에 "payments" 아이콘 추가 ## 4. i18n config/locales/ko.yml에 추가: ```yaml admin: nav: custom_payments: "커스텀 결제" custom_payment_links: title: "커스텀 결제 링크" subtitle: "총 %{count}개의 커스텀 결제 링크" new_link: "새 결제 링크 만들기" create_title: "커스텀 결제 링크 생성" create_description: "관리자가 임의의 금액으로 결제 링크를 생성할 수 있습니다..." form: title: "상품명" title_placeholder: "예: 1:1 코칭 컨설팅" amount: "결제 금액 (원)" amount_placeholder: "예: 150000" description: "상세 설명 (선택)" description_placeholder: "예: 2시간 1:1 강점 코칭 세션" expires_in: "유효 기간" submit: "결제 링크 생성" status: pending: "대기중" completed: "결제완료" canceled: "취소됨" actions: copy: "복사" cancel: "취소" qr: "QR" flash: created: "결제 링크가 생성되었습니다." canceled: "결제 링크가 취소되었습니다." copied: "링크가 클립보드에 복사되었습니다." ``` ## 5. 기존 UI 패턴 참고 - 테이블: bg-white rounded-xl shadow-sm border, thead bg-surface-emphasis, tr hover:bg-surface-emphasis - 배지: inline-flex items-center px-2 py-0.5 rounded text-xs font-medium - 버튼: px-4 py-2 bg-admin-primary text-white rounded-lg - 폼: w-full rounded-lg border-border-default focus:border-admin-primary ## 담당 파일 - app/controllers/admin/custom_payment_links_controller.rb - app/views/admin/custom_payment_links/index.html.erb - app/views/admin/custom_payment_links/new.html.erb - app/views/admin/shared/_sidebar.html.erb (수정) - app/views/admin/shared/_nav_item.html.erb (아이콘 추가) - config/locales/ko.yml (admin 섹션 수정) - app/javascript/controllers/clipboard_controller.js (새 파일) - app/javascript/controllers/qr_modal_controller.js (새 파일) - test/controllers/admin/custom_payment_links_controller_test.rb ## 완료 기준 - Admin 결제 링크 목록 페이지 작동 - 생성 폼 작동 (유효성 검증 포함) - 취소 기능 작동 - 복사 기능 작동 - QR 코드 표시 - 사이드바 메뉴 표시 - 컨트롤러 테스트 통과
고객 결제 페이지 + Toss Payments 연동
## 작업 내용 고객이 커스텀 결제 링크를 통해 접근하여 Toss Payments로 결제할 수 있는 공개 페이지를 구현합니다. ## 1. CustomPaymentsController 로그인 불요 (공개 페이지). ApplicationController 상속하되 skip_before_action :require_authentication. ### show (GET /pay/:token) - CustomPaymentLink.find_by!(token: params[:token]) - 만료/취소/완료 여부 체크 - 만료됨 → expired 상태 표시 - 취소됨 → canceled 상태 표시 - 결제완료 → 이미 완료 메시지 - 대기중 → 결제 폼 표시 ### checkout (POST /pay/:token/checkout) - 결제 링크 유효성 검증 (pending? && !expired?) - 고객용 임시 User 생성 또는 guest 처리 - 실제로는 Toss에서 결제 처리하므로 user 없이도 가능 - Payment 생성 시 user_id는 결제 링크 생성자(admin)의 ID 사용 - Payments::CheckoutService 호출: - payment_type: "CUSTOM" - amount: custom_payment_link.amount - description: custom_payment_link.title - metadata: { custom_payment_link_id: link.id } - JSON 응답으로 Toss SDK용 데이터 반환 ### success (GET /pay/:token/success) - Payments::ConfirmationService로 결제 승인 - CustomPaymentLink 상태를 completed로 변경 - payment 연결, paid_at 설정 - 성공 페이지 표시 ### fail (GET /pay/:token/fail) - 실패 메시지 표시 - 다시 시도 링크 제공 ## 2. 뷰 ### show.html.erb - 결제 페이지 - 독립 레이아웃 (admin/user 레이아웃 아님) - 깔끔한 결제 카드 형태: - 9WAY 로고 - 상품명 (title) - 상세 설명 (description) - 결제 금액 (amount) - 큰 글씨 - "결제하기" 버튼 - Toss Payments SDK 로드: ```html <script src="https://js.tosspayments.com/v2/standard"></script> ``` - Stimulus 또는 vanilla JS로 Toss 결제 위젯 초기화 - 결제 시퀀스: 1. "결제하기" 클릭 2. POST /pay/:token/checkout으로 주문 생성 3. 응답의 orderId, amount로 Toss requestPayment() 호출 4. 성공 → /pay/:token/success?paymentKey=...&orderId=...&amount=... 5. 실패 → /pay/:token/fail?code=...&message=... - 만료/취소/완료 시 적절한 메시지 표시 ### success.html.erb - 결제 성공 - 성공 아이콘 + "결제가 완료되었습니다" 메시지 - 결제 정보 요약 (상품명, 금액, 결제일시) ### 기존 참고 코드 - app/controllers/payments_controller.rb의 checkout/success/fail 패턴 - app/services/payments/checkout_service.rb의 결제 생성 로직 - app/services/payments/confirmation_service.rb의 결제 승인 로직 - 기존 Toss client_key: Rails.application.config.toss_payments.client_key ## 3. 레이아웃 - 별도의 minimal 레이아웃 사용: layout "custom_payment" - app/views/layouts/custom_payment.html.erb 생성 - 심플한 흰색 배경 + 가운데 정렬 카드 ## 4. 라우팅 라우팅은 "백엔드" 에이전트가 처리합니다. 다음 경로를 사용합니다: - GET /pay/:token → custom_payments#show - POST /pay/:token/checkout → custom_payments#checkout - GET /pay/:token/success → custom_payments#success - GET /pay/:token/fail → custom_payments#fail ## 담당 파일 - app/controllers/custom_payments_controller.rb - app/views/custom_payments/show.html.erb - app/views/custom_payments/success.html.erb - app/views/layouts/custom_payment.html.erb - app/javascript/controllers/toss_custom_payment_controller.js - test/controllers/custom_payments_controller_test.rb ## 완료 기준 - 유효한 토큰으로 결제 페이지 접근 가능 - 만료/취소/완료 상태 적절히 표시 - Toss Payments 결제 플로우 동작 - 결제 성공 시 CustomPaymentLink 상태 업데이트 - 컨트롤러 테스트 통과