77import com .backend .domain .payment .dto .response .PaymentMethodDeleteResponse ;
88import com .backend .domain .payment .dto .request .PaymentMethodEditRequest ;
99import com .backend .domain .payment .dto .response .PaymentMethodResponse ;
10- import com . backend . domain . payment . dto . response . TossConfirmResultResponse ;
10+ import jakarta . servlet . http . HttpServletRequest ;
1111import com .backend .domain .payment .dto .response .TossIssueBillingKeyResponse ;
1212import com .backend .domain .payment .service .PaymentMethodService ;
1313import com .backend .domain .payment .service .TossBillingClientService ;
@@ -166,20 +166,24 @@ public RsData<PaymentMethodDeleteResponse> delete(
166166 // Toss success/fail 리다이렉트가 도달하는 콜백
167167 @ GetMapping ("/toss/confirm-callback" )
168168 public ResponseEntity <Void > confirmCallback (
169+ HttpServletRequest request ,
169170 @ RequestParam (required = false ) String customerKey ,
170171 @ RequestParam (required = false ) String authKey ,
171172 @ RequestParam (required = false , defaultValue = "" ) String result
172173 ) {
173174 try {
174- String res = (result == null ) ? "" : result .toLowerCase ();
175- if (!res .startsWith ("success" )) {
175+ String normalized = (result == null ) ? "" : result ;
176+ int q = normalized .indexOf ('?' );
177+ if (q >= 0 ) normalized = normalized .substring (0 , q );
178+
179+ if (!"success" .equalsIgnoreCase (normalized )) {
176180 return redirect ("/wallet?billing=fail&reason=result_not_success" );
177181 }
178182 if (customerKey == null || authKey == null ) {
179183 return redirect ("/wallet?billing=fail&reason=missing_param" );
180184 }
181185
182- log .info ("[TOSS CALLBACK] result ={}, customerKey={}, authKey={}" , result , customerKey , mask (authKey ));
186+ log .info ("[TOSS CALLBACK] rawQuery ={}, customerKey={}, authKey(mask) ={}" , request . getQueryString () , customerKey , mask (authKey ));
183187
184188 TossIssueBillingKeyResponse confirm = tossBillingClientService .issueBillingKey (customerKey , authKey );
185189 Long memberId = parseMemberIdFromCustomerKey (customerKey );
@@ -188,7 +192,8 @@ public ResponseEntity<Void> confirmCallback(
188192 log .info ("[TOSS CALLBACK] save success: billingKey={}, brand={}, last4={}" ,
189193 confirm .getBillingKey (), confirm .getBrand (), confirm .getLast4 ());
190194
191- return redirect ("/wallet" ); // 성공은 깔끔하게 /wallet
195+ String redirectTo = extractRedirectTo (request .getQueryString ());
196+ return redirect (redirectTo != null ? redirectTo : "/wallet" );
192197
193198 } catch (org .springframework .web .server .ResponseStatusException e ) {
194199 log .warn ("[TOSS CALLBACK] pg error: {}" , e .getReason (), e );
@@ -209,6 +214,17 @@ private ResponseEntity<Void> redirect(String pathAndQuery) {
209214
210215 private String mask (String s ){ return (s ==null ||s .length ()<6 )?s :s .substring (0 ,3 )+"***" +s .substring (s .length ()-3 ); }
211216
217+ private String extractRedirectTo (String qs ) {
218+ if (qs == null ) return null ;
219+ java .util .regex .Matcher m = java .util .regex .Pattern
220+ .compile ("redirectTo=([^&]+)" )
221+ .matcher (qs );
222+ if (m .find ()) {
223+ return java .net .URLDecoder .decode (m .group (1 ), java .nio .charset .StandardCharsets .UTF_8 );
224+ }
225+ return null ;
226+ }
227+
212228 private Long parseMemberIdFromCustomerKey (String customerKey ) {
213229 if (customerKey != null && customerKey .startsWith ("user-" )) {
214230 return Long .parseLong (customerKey .substring ("user-" .length ()));
0 commit comments