kcppayments_rails 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +49 -0
- data/Rakefile +4 -0
- data/lib/generators/kcppayments_rails/install/install_generator.rb +21 -0
- data/lib/generators/kcppayments_rails/install/templates/kcp_controller.js +54 -0
- data/lib/generators/kcppayments_rails/install/templates/kcppayments_rails.rb +14 -0
- data/lib/kcppayments_rails/client.rb +49 -0
- data/lib/kcppayments_rails/configuration.rb +24 -0
- data/lib/kcppayments_rails/engine.rb +21 -0
- data/lib/kcppayments_rails/helpers/kcp_helper.rb +33 -0
- data/lib/kcppayments_rails/version.rb +5 -0
- data/lib/kcppayments_rails.rb +26 -0
- data/sig/kcppayments_rails.rbs +4 -0
- data/test_app/.dockerignore +51 -0
- data/test_app/.github/dependabot.yml +12 -0
- data/test_app/.github/workflows/ci.yml +55 -0
- data/test_app/.gitignore +87 -0
- data/test_app/.kamal/hooks/docker-setup.sample +3 -0
- data/test_app/.kamal/hooks/post-app-boot.sample +3 -0
- data/test_app/.kamal/hooks/post-deploy.sample +14 -0
- data/test_app/.kamal/hooks/post-proxy-reboot.sample +3 -0
- data/test_app/.kamal/hooks/pre-app-boot.sample +3 -0
- data/test_app/.kamal/hooks/pre-build.sample +51 -0
- data/test_app/.kamal/hooks/pre-connect.sample +47 -0
- data/test_app/.kamal/hooks/pre-deploy.sample +122 -0
- data/test_app/.kamal/hooks/pre-proxy-reboot.sample +3 -0
- data/test_app/.kamal/secrets +17 -0
- data/test_app/.rubocop.yml +8 -0
- data/test_app/.ruby-version +1 -0
- data/test_app/Dockerfile +72 -0
- data/test_app/Gemfile +60 -0
- data/test_app/Gemfile.lock +370 -0
- data/test_app/README.md +181 -0
- data/test_app/Rakefile +6 -0
- data/test_app/app/assets/images/.keep +0 -0
- data/test_app/app/assets/stylesheets/application.css +10 -0
- data/test_app/app/controllers/application_controller.rb +4 -0
- data/test_app/app/controllers/concerns/.keep +0 -0
- data/test_app/app/controllers/orders_controller.rb +31 -0
- data/test_app/app/controllers/payments_controller.rb +120 -0
- data/test_app/app/helpers/application_helper.rb +2 -0
- data/test_app/app/helpers/orders_helper.rb +2 -0
- data/test_app/app/helpers/payments_helper.rb +2 -0
- data/test_app/app/javascript/application.js +3 -0
- data/test_app/app/javascript/controllers/application.js +9 -0
- data/test_app/app/javascript/controllers/hello_controller.js +7 -0
- data/test_app/app/javascript/controllers/index.js +4 -0
- data/test_app/app/javascript/controllers/kcp_controller.js +310 -0
- data/test_app/app/jobs/application_job.rb +7 -0
- data/test_app/app/mailers/application_mailer.rb +4 -0
- data/test_app/app/models/application_record.rb +3 -0
- data/test_app/app/models/concerns/.keep +0 -0
- data/test_app/app/models/order.rb +67 -0
- data/test_app/app/views/layouts/application.html.erb +28 -0
- data/test_app/app/views/layouts/mailer.html.erb +13 -0
- data/test_app/app/views/layouts/mailer.text.erb +1 -0
- data/test_app/app/views/orders/create.html.erb +2 -0
- data/test_app/app/views/orders/index.html.erb +41 -0
- data/test_app/app/views/orders/new.html.erb +44 -0
- data/test_app/app/views/orders/show.html.erb +36 -0
- data/test_app/app/views/payments/_receipt_links.html.erb +31 -0
- data/test_app/app/views/payments/callback.html.erb +2 -0
- data/test_app/app/views/payments/create.html.erb +2 -0
- data/test_app/app/views/payments/failure.html.erb +20 -0
- data/test_app/app/views/payments/new.html.erb +145 -0
- data/test_app/app/views/payments/receipt.html.erb +156 -0
- data/test_app/app/views/payments/success.html.erb +34 -0
- data/test_app/app/views/pwa/manifest.json.erb +22 -0
- data/test_app/app/views/pwa/service-worker.js +26 -0
- data/test_app/bin/brakeman +7 -0
- data/test_app/bin/dev +2 -0
- data/test_app/bin/docker-entrypoint +14 -0
- data/test_app/bin/importmap +4 -0
- data/test_app/bin/jobs +6 -0
- data/test_app/bin/kamal +16 -0
- data/test_app/bin/rails +4 -0
- data/test_app/bin/rake +4 -0
- data/test_app/bin/rubocop +8 -0
- data/test_app/bin/setup +65 -0
- data/test_app/bin/thrust +5 -0
- data/test_app/config/application.rb +42 -0
- data/test_app/config/boot.rb +4 -0
- data/test_app/config/cable.yml +17 -0
- data/test_app/config/cache.yml +16 -0
- data/test_app/config/credentials.yml.enc +1 -0
- data/test_app/config/database.yml +41 -0
- data/test_app/config/deploy.yml +116 -0
- data/test_app/config/environment.rb +5 -0
- data/test_app/config/environments/development.rb +74 -0
- data/test_app/config/environments/production.rb +90 -0
- data/test_app/config/environments/test.rb +53 -0
- data/test_app/config/importmap.rb +7 -0
- data/test_app/config/initializers/assets.rb +7 -0
- data/test_app/config/initializers/content_security_policy.rb +25 -0
- data/test_app/config/initializers/filter_parameter_logging.rb +8 -0
- data/test_app/config/initializers/inflections.rb +16 -0
- data/test_app/config/initializers/kcppayments_rails.rb +29 -0
- data/test_app/config/locales/en.yml +31 -0
- data/test_app/config/master.key +1 -0
- data/test_app/config/puma.rb +41 -0
- data/test_app/config/queue.yml +18 -0
- data/test_app/config/recurring.yml +15 -0
- data/test_app/config/routes.rb +29 -0
- data/test_app/config/storage.yml +34 -0
- data/test_app/config.ru +6 -0
- data/test_app/db/cable_schema.rb +11 -0
- data/test_app/db/cache_schema.rb +14 -0
- data/test_app/db/migrate/20250827075913_create_orders.rb +16 -0
- data/test_app/db/migrate/20250827121258_add_kcp_fields_to_orders.rb +6 -0
- data/test_app/db/queue_schema.rb +129 -0
- data/test_app/db/schema.rb +28 -0
- data/test_app/db/seeds.rb +80 -0
- data/test_app/lib/tasks/.keep +0 -0
- data/test_app/log/.keep +0 -0
- data/test_app/public/.well-known/appspecific/com.chrome.devtools.json +1 -0
- data/test_app/public/400.html +114 -0
- data/test_app/public/404.html +114 -0
- data/test_app/public/406-unsupported-browser.html +114 -0
- data/test_app/public/422.html +114 -0
- data/test_app/public/500.html +114 -0
- data/test_app/public/icon.png +0 -0
- data/test_app/public/icon.svg +3 -0
- data/test_app/public/robots.txt +1 -0
- data/test_app/script/.keep +0 -0
- data/test_app/tmp/.keep +0 -0
- data/test_app/tmp/restart.txt +0 -0
- data/test_app/vendor/.keep +0 -0
- data/test_app/vendor/javascript/.keep +0 -0
- metadata +184 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<h1>KCP 결제</h1>
|
|
2
|
+
|
|
3
|
+
<div style="background-color: #f9f9f9; padding: 20px; border-radius: 5px; margin-bottom: 20px;">
|
|
4
|
+
<h2>주문 정보</h2>
|
|
5
|
+
<p><strong>주문번호:</strong> <%= @order.order_no %></p>
|
|
6
|
+
<p><strong>상품명:</strong> <%= @order.product_name %></p>
|
|
7
|
+
<p><strong>결제금액:</strong> <%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
|
|
8
|
+
<% if @order.tax_free_amount.present? && @order.tax_free_amount > 0 %>
|
|
9
|
+
<p><strong>면세금액:</strong> <%= number_to_currency(@order.tax_free_amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
|
|
10
|
+
<% end %>
|
|
11
|
+
<p><strong>구매자명:</strong> <%= @order.buyer_name %></p>
|
|
12
|
+
<p><strong>구매자 이메일:</strong> <%= @order.buyer_email %></p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<!-- KCP 결제 스크립트 로드 -->
|
|
16
|
+
<%= kcp_script_tag %>
|
|
17
|
+
|
|
18
|
+
<!-- KCP 결제 폼 -->
|
|
19
|
+
<form name="kcp_pay_form"
|
|
20
|
+
id="kcp_pay_form"
|
|
21
|
+
method="post"
|
|
22
|
+
action="<%= callback_payments_url %>"
|
|
23
|
+
target="_self"
|
|
24
|
+
data-controller="kcp"
|
|
25
|
+
data-kcp-order-id-value="<%= @order.order_no %>"
|
|
26
|
+
data-kcp-amount-value="<%= @order.amount %>"
|
|
27
|
+
data-kcp-buyer-name-value="<%= @order.buyer_name %>"
|
|
28
|
+
data-kcp-buyer-email-value="<%= @order.buyer_email %>"
|
|
29
|
+
data-kcp-product-name-value="<%= @order.product_name %>"
|
|
30
|
+
data-kcp-return-url-value="<%= callback_payments_url %>"
|
|
31
|
+
data-kcp-tax-free-amount-value="<%= @order.tax_free_amount %>">
|
|
32
|
+
|
|
33
|
+
<!-- CSRF 토큰 -->
|
|
34
|
+
<%= hidden_field_tag :authenticity_token, form_authenticity_token %>
|
|
35
|
+
|
|
36
|
+
<!-- KCP 필수 hidden 필드들 -->
|
|
37
|
+
<%= hidden_field_tag :site_cd, KcppaymentsRails.configuration.site_cd %>
|
|
38
|
+
<%= hidden_field_tag :site_name, "테스트몰" %>
|
|
39
|
+
<%= hidden_field_tag :ordr_idxx, @order.order_no %>
|
|
40
|
+
<%= hidden_field_tag :good_name, @order.product_name %>
|
|
41
|
+
<%= hidden_field_tag :good_mny, @order.amount %>
|
|
42
|
+
<%= hidden_field_tag :curr_cd, "410" %>
|
|
43
|
+
<%= hidden_field_tag :shop_user_id, @order.buyer_email %>
|
|
44
|
+
<%= hidden_field_tag :buyr_name, @order.buyer_name %>
|
|
45
|
+
<%= hidden_field_tag :buyr_mail, @order.buyer_email %>
|
|
46
|
+
<%= hidden_field_tag :buyr_tel1, "010" %>
|
|
47
|
+
<%= hidden_field_tag :buyr_tel2, "1234" %>
|
|
48
|
+
<%= hidden_field_tag :buyr_tel3, "5678" %>
|
|
49
|
+
<%= hidden_field_tag :mod_type, "0" %>
|
|
50
|
+
<%= hidden_field_tag :mod_ip, request.remote_ip %>
|
|
51
|
+
<%= hidden_field_tag :mod_desc, "주문변경" %>
|
|
52
|
+
|
|
53
|
+
<!-- 결제 처리를 위한 추가 필드 -->
|
|
54
|
+
<%= hidden_field_tag :Ret_URL, callback_payments_url %>
|
|
55
|
+
<%= hidden_field_tag :ReturnURL, callback_payments_url %>
|
|
56
|
+
<%= hidden_field_tag :approval_key, "" %>
|
|
57
|
+
<%= hidden_field_tag :traceNo, "" %>
|
|
58
|
+
|
|
59
|
+
<!-- 수령자 정보 -->
|
|
60
|
+
<%= hidden_field_tag :rcvr_name, @order.buyer_name %>
|
|
61
|
+
<%= hidden_field_tag :rcvr_tel1, "010" %>
|
|
62
|
+
<%= hidden_field_tag :rcvr_tel2, "1234" %>
|
|
63
|
+
<%= hidden_field_tag :rcvr_tel3, "5678" %>
|
|
64
|
+
<%= hidden_field_tag :rcvr_mail, @order.buyer_email %>
|
|
65
|
+
<%= hidden_field_tag :rcvr_zipx, "12345" %>
|
|
66
|
+
<%= hidden_field_tag :rcvr_add1, "서울시 강남구" %>
|
|
67
|
+
<%= hidden_field_tag :rcvr_add2, "테스트 주소" %>
|
|
68
|
+
|
|
69
|
+
<!-- 결제 옵션 -->
|
|
70
|
+
<%= hidden_field_tag :quotaopt, "12" %>
|
|
71
|
+
<%= hidden_field_tag :escw_used, "N" %>
|
|
72
|
+
<%= hidden_field_tag :card_cop, "10300" %>
|
|
73
|
+
<%= hidden_field_tag :kcp_cert, "true" %>
|
|
74
|
+
<%= hidden_field_tag :disp_tax_yn, "N" %>
|
|
75
|
+
<%= hidden_field_tag :vcnt_expire_term, "" %>
|
|
76
|
+
<%= hidden_field_tag :vcnt_expire_term_time, "" %>
|
|
77
|
+
<%= hidden_field_tag :bask_cntx, "0" %>
|
|
78
|
+
<%= hidden_field_tag :eng_flag, "N" %>
|
|
79
|
+
<%= hidden_field_tag :user_agent, "" %>
|
|
80
|
+
<%= hidden_field_tag :ipgm_name, "kcp" %>
|
|
81
|
+
<%= hidden_field_tag :epay_no, "" %>
|
|
82
|
+
|
|
83
|
+
<!-- KCP JavaScript가 요구하는 추가 필수 필드들 -->
|
|
84
|
+
<%= hidden_field_tag :encoding_trans, "UTF-8" %>
|
|
85
|
+
<%= hidden_field_tag :req_tx, "pay" %>
|
|
86
|
+
<%= hidden_field_tag :shop_name, "테스트몰" %>
|
|
87
|
+
<%= hidden_field_tag :lang_flag, "KOR" %>
|
|
88
|
+
<%= hidden_field_tag :site_logo, "" %>
|
|
89
|
+
<%= hidden_field_tag :req_ip, request.remote_ip %>
|
|
90
|
+
<%= hidden_field_tag :complex_yn, "N" %>
|
|
91
|
+
<%= hidden_field_tag :kcp_noti, "" %>
|
|
92
|
+
<%= hidden_field_tag :van_code, "" %>
|
|
93
|
+
|
|
94
|
+
<!-- 세금 관련 -->
|
|
95
|
+
<% if @order.tax_free_amount.present? && @order.tax_free_amount > 0 %>
|
|
96
|
+
<%= hidden_field_tag :tax_flag, "TG03" %>
|
|
97
|
+
<%= hidden_field_tag :comm_tax_mny, (@order.amount - @order.tax_free_amount) %>
|
|
98
|
+
<%= hidden_field_tag :comm_free_mny, @order.tax_free_amount %>
|
|
99
|
+
<% else %>
|
|
100
|
+
<%= hidden_field_tag :tax_flag, "TG01" %>
|
|
101
|
+
<%= hidden_field_tag :comm_tax_mny, @order.amount %>
|
|
102
|
+
<%= hidden_field_tag :comm_free_mny, "0" %>
|
|
103
|
+
<% end %>
|
|
104
|
+
|
|
105
|
+
<!-- KCP 처리 결과 필드들 -->
|
|
106
|
+
<%= hidden_field_tag :res_cd, "" %>
|
|
107
|
+
<%= hidden_field_tag :res_msg, "" %>
|
|
108
|
+
<%= hidden_field_tag :tno, "" %>
|
|
109
|
+
<%= hidden_field_tag :trace_no, "" %>
|
|
110
|
+
<%= hidden_field_tag :enc_info, "" %>
|
|
111
|
+
<%= hidden_field_tag :enc_data, "" %>
|
|
112
|
+
<%= hidden_field_tag :ret_pay_method, "" %>
|
|
113
|
+
<%= hidden_field_tag :trad_date, "" %>
|
|
114
|
+
<%= hidden_field_tag :trad_time, "" %>
|
|
115
|
+
<%= hidden_field_tag :bank_name, "" %>
|
|
116
|
+
<%= hidden_field_tag :bank_issu, "" %>
|
|
117
|
+
|
|
118
|
+
<!-- 결제 수단 선택 -->
|
|
119
|
+
<%= hidden_field_tag :pay_method, "100000000000" %>
|
|
120
|
+
|
|
121
|
+
<div style="margin-bottom: 20px;">
|
|
122
|
+
<h3>결제 수단 선택</h3>
|
|
123
|
+
<label>
|
|
124
|
+
<%= radio_button_tag "pay_method_display", "card", true, onclick: "document.getElementsByName('pay_method')[0].value='100000000000'" %>
|
|
125
|
+
신용카드
|
|
126
|
+
</label>
|
|
127
|
+
<label style="margin-left: 20px;">
|
|
128
|
+
<%= radio_button_tag "pay_method_display", "transfer", false, onclick: "document.getElementsByName('pay_method')[0].value='010000000000'" %>
|
|
129
|
+
계좌이체
|
|
130
|
+
</label>
|
|
131
|
+
<label style="margin-left: 20px;">
|
|
132
|
+
<%= radio_button_tag "pay_method_display", "vbank", false, onclick: "document.getElementsByName('pay_method')[0].value='001000000000'" %>
|
|
133
|
+
가상계좌
|
|
134
|
+
</label>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div>
|
|
138
|
+
<%= button_tag "결제하기",
|
|
139
|
+
type: "button",
|
|
140
|
+
data: { action: "click->kcp#requestPayment" },
|
|
141
|
+
style: "padding: 10px 30px; background-color: #FF5722; color: white; border: none; cursor: pointer; font-size: 16px; border-radius: 3px;" %>
|
|
142
|
+
|
|
143
|
+
<%= link_to "취소", order_path(@order), style: "margin-left: 10px;" %>
|
|
144
|
+
</div>
|
|
145
|
+
</form>
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<title>결제 영수증</title>
|
|
5
|
+
<meta charset="utf-8">
|
|
6
|
+
<style>
|
|
7
|
+
body {
|
|
8
|
+
font-family: Arial, sans-serif;
|
|
9
|
+
margin: 20px;
|
|
10
|
+
background-color: white;
|
|
11
|
+
}
|
|
12
|
+
.receipt-container {
|
|
13
|
+
max-width: 600px;
|
|
14
|
+
margin: 0 auto;
|
|
15
|
+
border: 1px solid #ddd;
|
|
16
|
+
padding: 30px;
|
|
17
|
+
background-color: white;
|
|
18
|
+
}
|
|
19
|
+
.header {
|
|
20
|
+
text-align: center;
|
|
21
|
+
border-bottom: 2px solid #333;
|
|
22
|
+
padding-bottom: 20px;
|
|
23
|
+
margin-bottom: 30px;
|
|
24
|
+
}
|
|
25
|
+
.header h1 {
|
|
26
|
+
color: #333;
|
|
27
|
+
margin: 0;
|
|
28
|
+
font-size: 28px;
|
|
29
|
+
}
|
|
30
|
+
.receipt-info {
|
|
31
|
+
margin-bottom: 30px;
|
|
32
|
+
}
|
|
33
|
+
.info-row {
|
|
34
|
+
display: flex;
|
|
35
|
+
justify-content: space-between;
|
|
36
|
+
padding: 8px 0;
|
|
37
|
+
border-bottom: 1px dotted #ccc;
|
|
38
|
+
}
|
|
39
|
+
.info-label {
|
|
40
|
+
font-weight: bold;
|
|
41
|
+
color: #555;
|
|
42
|
+
}
|
|
43
|
+
.info-value {
|
|
44
|
+
color: #333;
|
|
45
|
+
}
|
|
46
|
+
.amount {
|
|
47
|
+
font-size: 20px;
|
|
48
|
+
font-weight: bold;
|
|
49
|
+
color: #2e7d32;
|
|
50
|
+
}
|
|
51
|
+
.footer {
|
|
52
|
+
margin-top: 40px;
|
|
53
|
+
padding-top: 20px;
|
|
54
|
+
border-top: 1px solid #ddd;
|
|
55
|
+
text-align: center;
|
|
56
|
+
color: #666;
|
|
57
|
+
font-size: 14px;
|
|
58
|
+
}
|
|
59
|
+
.print-btn {
|
|
60
|
+
text-align: center;
|
|
61
|
+
margin: 20px 0;
|
|
62
|
+
}
|
|
63
|
+
.print-btn button {
|
|
64
|
+
padding: 10px 30px;
|
|
65
|
+
background-color: #4CAF50;
|
|
66
|
+
color: white;
|
|
67
|
+
border: none;
|
|
68
|
+
border-radius: 5px;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
font-size: 16px;
|
|
71
|
+
}
|
|
72
|
+
@media print {
|
|
73
|
+
.print-btn {
|
|
74
|
+
display: none;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<div class="receipt-container">
|
|
81
|
+
<% if @order.nil? %>
|
|
82
|
+
<div class="header">
|
|
83
|
+
<h1>❌ 오류</h1>
|
|
84
|
+
<p>주문 정보를 찾을 수 없습니다.</p>
|
|
85
|
+
</div>
|
|
86
|
+
<script>
|
|
87
|
+
alert('주문 정보를 찾을 수 없습니다.');
|
|
88
|
+
window.close();
|
|
89
|
+
</script>
|
|
90
|
+
<% else %>
|
|
91
|
+
<div class="header">
|
|
92
|
+
<h1>🧾 결제 영수증</h1>
|
|
93
|
+
<p>Receipt / 領収書</p>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="receipt-info">
|
|
97
|
+
<div class="info-row">
|
|
98
|
+
<span class="info-label">주문번호 (Order No.):</span>
|
|
99
|
+
<span class="info-value"><%= @order.order_no %></span>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="info-row">
|
|
102
|
+
<span class="info-label">결제일시 (Payment Date):</span>
|
|
103
|
+
<span class="info-value"><%= @order.updated_at.strftime("%Y년 %m월 %d일 %H:%M") %></span>
|
|
104
|
+
</div>
|
|
105
|
+
<div class="info-row">
|
|
106
|
+
<span class="info-label">상품명 (Product):</span>
|
|
107
|
+
<span class="info-value"><%= @order.product_name %></span>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="info-row">
|
|
110
|
+
<span class="info-label">구매자 (Buyer):</span>
|
|
111
|
+
<span class="info-value"><%= @order.buyer_name %></span>
|
|
112
|
+
</div>
|
|
113
|
+
<div class="info-row">
|
|
114
|
+
<span class="info-label">이메일 (Email):</span>
|
|
115
|
+
<span class="info-value"><%= @order.buyer_email %></span>
|
|
116
|
+
</div>
|
|
117
|
+
<% if @order.payment_method.present? %>
|
|
118
|
+
<div class="info-row">
|
|
119
|
+
<span class="info-label">결제수단 (Payment Method):</span>
|
|
120
|
+
<span class="info-value"><%= @order.payment_method %></span>
|
|
121
|
+
</div>
|
|
122
|
+
<% end %>
|
|
123
|
+
<div class="info-row">
|
|
124
|
+
<span class="info-label">결제상태 (Status):</span>
|
|
125
|
+
<span class="info-value">
|
|
126
|
+
<% if @order.status == "paid" %>
|
|
127
|
+
<span style="color: #2e7d32;">✓ 결제완료 (Paid)</span>
|
|
128
|
+
<% else %>
|
|
129
|
+
<span style="color: #d32f2f;"><%= @order.status %></span>
|
|
130
|
+
<% end %>
|
|
131
|
+
</span>
|
|
132
|
+
</div>
|
|
133
|
+
<div class="info-row" style="font-size: 18px; font-weight: bold; border-bottom: 2px solid #333; padding: 15px 0; margin-top: 20px;">
|
|
134
|
+
<span class="info-label">결제금액 (Amount):</span>
|
|
135
|
+
<span class="info-value amount"><%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></span>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div class="print-btn">
|
|
140
|
+
<button onclick="window.print()">🖨️ 인쇄하기</button>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<div class="footer">
|
|
144
|
+
<p>KCP Payments Rails - 전자결제 서비스</p>
|
|
145
|
+
<p>이 영수증은 전자결제 완료를 증명하는 문서입니다.</p>
|
|
146
|
+
<p>This receipt serves as proof of electronic payment completion.</p>
|
|
147
|
+
</div>
|
|
148
|
+
<% end %>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<script>
|
|
152
|
+
// 자동 프린트 (선택사항)
|
|
153
|
+
// window.onload = function() { window.print(); }
|
|
154
|
+
</script>
|
|
155
|
+
</body>
|
|
156
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<h1 style="color: green;">✓ 결제 성공</h1>
|
|
2
|
+
|
|
3
|
+
<% if @demo_mode %>
|
|
4
|
+
<div style="background-color: #fff3cd; padding: 15px; border-radius: 5px; border: 1px solid #ffc107; margin-bottom: 20px;">
|
|
5
|
+
<h4 style="color: #856404; margin-top: 0;">🧪 데모 모드</h4>
|
|
6
|
+
<p style="color: #856404; margin-bottom: 0;">
|
|
7
|
+
이는 젬 기능을 테스트하기 위한 데모 결제입니다.<br>
|
|
8
|
+
실제 KCP 계정을 설정하면 실제 결제가 처리됩니다.
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
<% end %>
|
|
12
|
+
|
|
13
|
+
<div style="background-color: #e8f5e9; padding: 20px; border-radius: 5px; border: 1px solid #4CAF50;">
|
|
14
|
+
<h2><%= @demo_mode ? "데모 결제가 성공적으로 완료되었습니다!" : "결제가 성공적으로 완료되었습니다!" %></h2>
|
|
15
|
+
|
|
16
|
+
<div style="margin-top: 20px;">
|
|
17
|
+
<p><strong>주문번호:</strong> <%= @order.order_no %></p>
|
|
18
|
+
<p><strong>상품명:</strong> <%= @order.product_name %></p>
|
|
19
|
+
<p><strong>결제금액:</strong> <%= number_to_currency(@order.amount, unit: "₩", format: "%u%n", delimiter: ",", precision: 0) %></p>
|
|
20
|
+
<p><strong>구매자:</strong> <%= @order.buyer_name %> (<%= @order.buyer_email %>)</p>
|
|
21
|
+
<% if @order.payment_method.present? %>
|
|
22
|
+
<p><strong>결제수단:</strong> <%= @order.payment_method %></p>
|
|
23
|
+
<% end %>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<div style="margin-top: 20px;">
|
|
28
|
+
<%= link_to "주문 상세보기", order_path(@order),
|
|
29
|
+
style: "padding: 10px 20px;margin-right: 10px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 3px;" %>
|
|
30
|
+
|
|
31
|
+
<%= render 'receipt_links', order: @order %>
|
|
32
|
+
|
|
33
|
+
<%= link_to "주문 목록", orders_path, style: "margin-left: 10px; margin-top: 15px; display: inline-block;" %>
|
|
34
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "TestApp",
|
|
3
|
+
"icons": [
|
|
4
|
+
{
|
|
5
|
+
"src": "/icon.png",
|
|
6
|
+
"type": "image/png",
|
|
7
|
+
"sizes": "512x512"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"src": "/icon.png",
|
|
11
|
+
"type": "image/png",
|
|
12
|
+
"sizes": "512x512",
|
|
13
|
+
"purpose": "maskable"
|
|
14
|
+
}
|
|
15
|
+
],
|
|
16
|
+
"start_url": "/",
|
|
17
|
+
"display": "standalone",
|
|
18
|
+
"scope": "/",
|
|
19
|
+
"description": "TestApp.",
|
|
20
|
+
"theme_color": "red",
|
|
21
|
+
"background_color": "red"
|
|
22
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// Add a service worker for processing Web Push notifications:
|
|
2
|
+
//
|
|
3
|
+
// self.addEventListener("push", async (event) => {
|
|
4
|
+
// const { title, options } = await event.data.json()
|
|
5
|
+
// event.waitUntil(self.registration.showNotification(title, options))
|
|
6
|
+
// })
|
|
7
|
+
//
|
|
8
|
+
// self.addEventListener("notificationclick", function(event) {
|
|
9
|
+
// event.notification.close()
|
|
10
|
+
// event.waitUntil(
|
|
11
|
+
// clients.matchAll({ type: "window" }).then((clientList) => {
|
|
12
|
+
// for (let i = 0; i < clientList.length; i++) {
|
|
13
|
+
// let client = clientList[i]
|
|
14
|
+
// let clientPath = (new URL(client.url)).pathname
|
|
15
|
+
//
|
|
16
|
+
// if (clientPath == event.notification.data.path && "focus" in client) {
|
|
17
|
+
// return client.focus()
|
|
18
|
+
// }
|
|
19
|
+
// }
|
|
20
|
+
//
|
|
21
|
+
// if (clients.openWindow) {
|
|
22
|
+
// return clients.openWindow(event.notification.data.path)
|
|
23
|
+
// }
|
|
24
|
+
// })
|
|
25
|
+
// )
|
|
26
|
+
// })
|
data/test_app/bin/dev
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/bin/bash -e
|
|
2
|
+
|
|
3
|
+
# Enable jemalloc for reduced memory usage and latency.
|
|
4
|
+
if [ -z "${LD_PRELOAD+x}" ]; then
|
|
5
|
+
LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit)
|
|
6
|
+
export LD_PRELOAD
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
# If running the rails server then create or migrate existing database
|
|
10
|
+
if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then
|
|
11
|
+
./bin/rails db:prepare
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
exec "${@}"
|
data/test_app/bin/jobs
ADDED
data/test_app/bin/kamal
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# This file was generated by Bundler.
|
|
6
|
+
#
|
|
7
|
+
# The application 'kamal' is installed as part of a gem, and
|
|
8
|
+
# this file is here to facilitate running it.
|
|
9
|
+
#
|
|
10
|
+
|
|
11
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
|
|
12
|
+
|
|
13
|
+
require "rubygems"
|
|
14
|
+
require "bundler/setup"
|
|
15
|
+
|
|
16
|
+
load Gem.bin_path("kamal", "kamal")
|
data/test_app/bin/rails
ADDED
data/test_app/bin/rake
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "rubygems"
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
|
|
5
|
+
# explicit rubocop config increases performance slightly while avoiding config confusion.
|
|
6
|
+
ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__))
|
|
7
|
+
|
|
8
|
+
load Gem.bin_path("rubocop", "rubocop")
|
data/test_app/bin/setup
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "fileutils"
|
|
3
|
+
|
|
4
|
+
APP_ROOT = File.expand_path("..", __dir__)
|
|
5
|
+
|
|
6
|
+
def system!(*args)
|
|
7
|
+
system(*args, exception: true)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
FileUtils.chdir APP_ROOT do
|
|
11
|
+
# This script is a way to set up or update your development environment automatically.
|
|
12
|
+
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
|
13
|
+
# Add necessary setup steps to this file.
|
|
14
|
+
|
|
15
|
+
puts "== Installing dependencies =="
|
|
16
|
+
system("bundle check") || system!("bundle install")
|
|
17
|
+
|
|
18
|
+
# puts "\n== Copying sample files =="
|
|
19
|
+
# unless File.exist?("config/database.yml")
|
|
20
|
+
# FileUtils.cp "config/database.yml.sample", "config/database.yml"
|
|
21
|
+
# end
|
|
22
|
+
|
|
23
|
+
puts "\n== Preparing database =="
|
|
24
|
+
system! "bin/rails db:prepare"
|
|
25
|
+
|
|
26
|
+
puts "\n== Removing old logs and tempfiles =="
|
|
27
|
+
system! "bin/rails log:clear tmp:clear"
|
|
28
|
+
|
|
29
|
+
unless ARGV.include?("--skip-server")
|
|
30
|
+
puts "\n== Starting development server =="
|
|
31
|
+
STDOUT.flush # flush the output before exec(2) so that it displays
|
|
32
|
+
exec "bin/dev"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def command_exists?(command)
|
|
38
|
+
system("command -v #{command} > /dev/null 2>&1")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Install puma-dev if it's not installed
|
|
42
|
+
|
|
43
|
+
unless command_exists?('brew')
|
|
44
|
+
puts "Homebrew is not installed. Please install Homebrew first."
|
|
45
|
+
exit 1
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
unless command_exists?('puma-dev')
|
|
49
|
+
puts "Installing puma-dev..."
|
|
50
|
+
system('brew install puma/puma/puma-dev')
|
|
51
|
+
|
|
52
|
+
puts "Running puma-dev setup..."
|
|
53
|
+
system('sudo puma-dev -setup')
|
|
54
|
+
|
|
55
|
+
puts "Installing puma-dev DNS settings..."
|
|
56
|
+
system('puma-dev -install')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Link the current app to puma-dev
|
|
60
|
+
|
|
61
|
+
puts "\n== Configuring Puma-dev to host https://app_name.test =="
|
|
62
|
+
app_name = File.basename(APP_ROOT)
|
|
63
|
+
|
|
64
|
+
system! "puma-dev link -n #{app_name}"
|
|
65
|
+
puts "puma-dev setup complete for #{app_name}.test"
|
data/test_app/bin/thrust
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require_relative "boot"
|
|
2
|
+
|
|
3
|
+
require "rails"
|
|
4
|
+
# Pick the frameworks you want:
|
|
5
|
+
require "active_model/railtie"
|
|
6
|
+
require "active_job/railtie"
|
|
7
|
+
require "active_record/railtie"
|
|
8
|
+
require "active_storage/engine"
|
|
9
|
+
require "action_controller/railtie"
|
|
10
|
+
require "action_mailer/railtie"
|
|
11
|
+
require "action_mailbox/engine"
|
|
12
|
+
require "action_text/engine"
|
|
13
|
+
require "action_view/railtie"
|
|
14
|
+
require "action_cable/engine"
|
|
15
|
+
# require "rails/test_unit/railtie"
|
|
16
|
+
|
|
17
|
+
# Require the gems listed in Gemfile, including any gems
|
|
18
|
+
# you've limited to :test, :development, or :production.
|
|
19
|
+
Bundler.require(*Rails.groups)
|
|
20
|
+
|
|
21
|
+
module TestApp
|
|
22
|
+
class Application < Rails::Application
|
|
23
|
+
# Initialize configuration defaults for originally generated Rails version.
|
|
24
|
+
config.load_defaults 8.0
|
|
25
|
+
|
|
26
|
+
# Please, add to the `ignore` list any other `lib` subdirectories that do
|
|
27
|
+
# not contain `.rb` files, or that should not be reloaded or eager loaded.
|
|
28
|
+
# Common ones are `templates`, `generators`, or `middleware`, for example.
|
|
29
|
+
config.autoload_lib(ignore: %w[assets tasks])
|
|
30
|
+
|
|
31
|
+
# Configuration for the application, engines, and railties goes here.
|
|
32
|
+
#
|
|
33
|
+
# These settings can be overridden in specific environments using the files
|
|
34
|
+
# in config/environments, which are processed later.
|
|
35
|
+
#
|
|
36
|
+
# config.time_zone = "Central Time (US & Canada)"
|
|
37
|
+
# config.eager_load_paths << Rails.root.join("extras")
|
|
38
|
+
|
|
39
|
+
# Don't generate system test files.
|
|
40
|
+
config.generators.system_tests = nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Async adapter only works within the same process, so for manually triggering cable updates from a console,
|
|
2
|
+
# and seeing results in the browser, you must do so from the web console (running inside the dev process),
|
|
3
|
+
# not a terminal started via bin/rails console! Add "console" to any action or any ERB template view
|
|
4
|
+
# to make the web console appear.
|
|
5
|
+
development:
|
|
6
|
+
adapter: async
|
|
7
|
+
|
|
8
|
+
test:
|
|
9
|
+
adapter: test
|
|
10
|
+
|
|
11
|
+
production:
|
|
12
|
+
adapter: solid_cable
|
|
13
|
+
connects_to:
|
|
14
|
+
database:
|
|
15
|
+
writing: cable
|
|
16
|
+
polling_interval: 0.1.seconds
|
|
17
|
+
message_retention: 1.day
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
default: &default
|
|
2
|
+
store_options:
|
|
3
|
+
# Cap age of oldest cache entry to fulfill retention policies
|
|
4
|
+
# max_age: <%= 60.days.to_i %>
|
|
5
|
+
max_size: <%= 256.megabytes %>
|
|
6
|
+
namespace: <%= Rails.env %>
|
|
7
|
+
|
|
8
|
+
development:
|
|
9
|
+
<<: *default
|
|
10
|
+
|
|
11
|
+
test:
|
|
12
|
+
<<: *default
|
|
13
|
+
|
|
14
|
+
production:
|
|
15
|
+
database: cache
|
|
16
|
+
<<: *default
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jCrJjZ8dSCROAGK7oEG5gfpi98YevpYNRCsK6Yf/MtF99C3wkuHwQdjQlvyFhT280PUYEyIoqENpQSV0xA+SR1RkVEX3uXeEfTZUEDI9XJln2v4vTKYNbDu3yjVX4b9G0Gg9Fd7MKYX85xLcL+TyBQfI/vftgTSFftDHaJOnlQ3prMeQIM2r/F4uMQ4FT+n+G/dcAHM7nkO3noPiDykaYvhgWDKgPY7C5+4faKx4uXQPXIlUFkiltTyuN9X5h5RuCHaaroUSz7ms3a0+gRUnFdy1udSL2ZxJJqNlRf7PlEtxxW7zuL/Ir8/oiZxXlVQeqg0MjBPhzc4KFKpkFRU3JW1iD5S1GMRoBh89q5+x3KrolyNKbVeTzUUQoOlbXyFXtbotULcR2Nm6tOTvG6OTEwpjIUslnbma/5okY8ux4kNagPJCU3M3RveER5uxJO8lbRG/i3G098OuEkSniPbxeXJT33xxuwGueC7AP8cNnEOsHyNMJr/JVgpC--idPG+LloS0T+ROJB--0YsDaiZ3JwOCTPyOTPgCow==
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# SQLite. Versions 3.8.0 and up are supported.
|
|
2
|
+
# gem install sqlite3
|
|
3
|
+
#
|
|
4
|
+
# Ensure the SQLite 3 gem is defined in your Gemfile
|
|
5
|
+
# gem "sqlite3"
|
|
6
|
+
#
|
|
7
|
+
default: &default
|
|
8
|
+
adapter: sqlite3
|
|
9
|
+
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
|
|
10
|
+
timeout: 5000
|
|
11
|
+
|
|
12
|
+
development:
|
|
13
|
+
<<: *default
|
|
14
|
+
database: storage/development.sqlite3
|
|
15
|
+
|
|
16
|
+
# Warning: The database defined as "test" will be erased and
|
|
17
|
+
# re-generated from your development database when you run "rake".
|
|
18
|
+
# Do not set this db to the same as development or production.
|
|
19
|
+
test:
|
|
20
|
+
<<: *default
|
|
21
|
+
database: storage/test.sqlite3
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Store production database in the storage/ directory, which by default
|
|
25
|
+
# is mounted as a persistent Docker volume in config/deploy.yml.
|
|
26
|
+
production:
|
|
27
|
+
primary:
|
|
28
|
+
<<: *default
|
|
29
|
+
database: storage/production.sqlite3
|
|
30
|
+
cache:
|
|
31
|
+
<<: *default
|
|
32
|
+
database: storage/production_cache.sqlite3
|
|
33
|
+
migrations_paths: db/cache_migrate
|
|
34
|
+
queue:
|
|
35
|
+
<<: *default
|
|
36
|
+
database: storage/production_queue.sqlite3
|
|
37
|
+
migrations_paths: db/queue_migrate
|
|
38
|
+
cable:
|
|
39
|
+
<<: *default
|
|
40
|
+
database: storage/production_cable.sqlite3
|
|
41
|
+
migrations_paths: db/cable_migrate
|