@@ -274,7 +274,7 @@ async fn submit_credential(
274274/// Maximum characters to include in a payment rejection reason.
275275const MAX_REJECTION_REASON_CHARS : usize = 500 ;
276276
277- /// Parse a non-200 response after payment submission into a descriptive error.
277+ /// Parse an error response after credential submission into a descriptive error.
278278fn parse_payment_rejection ( response : & HttpResponse ) -> PaymentError {
279279 let raw_reason = match response. body_string ( ) {
280280 Ok ( body) if !body. trim ( ) . is_empty ( ) => {
@@ -285,17 +285,19 @@ fn parse_payment_rejection(response: &HttpResponse) -> PaymentError {
285285 let reason = sanitize_for_terminal ( & raw_reason) ;
286286
287287 match response. status_code {
288- 500 ..= 599 => PaymentError :: UpstreamServerError {
288+ 402 => PaymentError :: PaymentRejected {
289289 reason,
290290 status_code : response. status_code ,
291291 } ,
292- 400 ..=499 if !is_retriable_payment_rejection ( response) => {
293- PaymentError :: UpstreamApplicationError {
294- reason,
295- status_code : response. status_code ,
296- }
297- }
298- _ => PaymentError :: PaymentRejected {
292+ 500 ..=599 => PaymentError :: PaymentResponseServerError {
293+ reason,
294+ status_code : response. status_code ,
295+ } ,
296+ 400 ..=499 => PaymentError :: PaymentResponseApplicationError {
297+ reason,
298+ status_code : response. status_code ,
299+ } ,
300+ _ => PaymentError :: PaymentResponseServerError {
299301 reason,
300302 status_code : response. status_code ,
301303 } ,
@@ -325,49 +327,75 @@ mod tests {
325327 }
326328
327329 #[ test]
328- fn test_parse_payment_rejection_classifies_upstream_application_error ( ) {
330+ fn test_parse_payment_rejection_classifies_application_response_error ( ) {
329331 let body = br#"{"error":"invalid_request","message":"missing input"}"# ;
330332 let resp = HttpResponse :: for_test ( 400 , body) ;
331333 let err = parse_payment_rejection ( & resp) ;
332334 match err {
333- PaymentError :: UpstreamApplicationError {
335+ PaymentError :: PaymentResponseApplicationError {
334336 reason,
335337 status_code,
336338 } => {
337339 assert ! ( reason. contains( "invalid_request" ) ) ;
338340 assert_eq ! ( status_code, 400 ) ;
339341 }
340- _ => panic ! ( "expected UpstreamApplicationError " ) ,
342+ _ => panic ! ( "expected PaymentResponseApplicationError " ) ,
341343 }
342344 }
343345
344346 #[ test]
345- fn test_parse_payment_rejection_classifies_upstream_server_error ( ) {
347+ fn test_parse_payment_rejection_classifies_server_response_error ( ) {
346348 let body = b"Transaction reverted" ;
347349 let resp = HttpResponse :: for_test ( 500 , body) ;
348350 let err = parse_payment_rejection ( & resp) ;
349351 match err {
350- PaymentError :: UpstreamServerError {
352+ PaymentError :: PaymentResponseServerError {
351353 reason,
352354 status_code,
353355 } => {
354356 assert_eq ! ( reason, "Transaction reverted" ) ;
355357 assert_eq ! ( status_code, 500 ) ;
356358 }
357- _ => panic ! ( "expected UpstreamServerError " ) ,
359+ _ => panic ! ( "expected PaymentResponseServerError " ) ,
358360 }
359361 }
360362
363+ #[ test]
364+ fn test_parse_payment_rejection_classifies_401_as_application_response_error ( ) {
365+ let resp = HttpResponse :: for_test ( 401 , b"login required" ) ;
366+ let err = parse_payment_rejection ( & resp) ;
367+ assert ! ( matches!(
368+ err,
369+ PaymentError :: PaymentResponseApplicationError {
370+ status_code: 401 ,
371+ ..
372+ }
373+ ) ) ;
374+ }
375+
376+ #[ test]
377+ fn test_parse_payment_rejection_classifies_403_as_application_response_error ( ) {
378+ let resp = HttpResponse :: for_test ( 403 , b"policy denied" ) ;
379+ let err = parse_payment_rejection ( & resp) ;
380+ assert ! ( matches!(
381+ err,
382+ PaymentError :: PaymentResponseApplicationError {
383+ status_code: 403 ,
384+ ..
385+ }
386+ ) ) ;
387+ }
388+
361389 #[ test]
362390 fn test_parse_payment_rejection_truncated ( ) {
363391 let body = "a" . repeat ( 600 ) ;
364392 let resp = HttpResponse :: for_test ( 500 , body. as_bytes ( ) ) ;
365393 let err = parse_payment_rejection ( & resp) ;
366394 match err {
367- PaymentError :: UpstreamServerError { reason, .. } => {
395+ PaymentError :: PaymentResponseServerError { reason, .. } => {
368396 assert_eq ! ( reason. len( ) , MAX_REJECTION_REASON_CHARS ) ;
369397 }
370- _ => panic ! ( "expected UpstreamServerError " ) ,
398+ _ => panic ! ( "expected PaymentResponseServerError " ) ,
371399 }
372400 }
373401
@@ -376,10 +404,10 @@ mod tests {
376404 let resp = HttpResponse :: for_test ( 500 , b"" ) ;
377405 let err = parse_payment_rejection ( & resp) ;
378406 match err {
379- PaymentError :: UpstreamServerError { reason, .. } => {
407+ PaymentError :: PaymentResponseServerError { reason, .. } => {
380408 assert_eq ! ( reason, "HTTP 500" ) ;
381409 }
382- _ => panic ! ( "expected UpstreamServerError " ) ,
410+ _ => panic ! ( "expected PaymentResponseServerError " ) ,
383411 }
384412 }
385413
@@ -388,10 +416,10 @@ mod tests {
388416 let resp = HttpResponse :: for_test ( 503 , b" \n \t " ) ;
389417 let err = parse_payment_rejection ( & resp) ;
390418 match err {
391- PaymentError :: UpstreamServerError { reason, .. } => {
419+ PaymentError :: PaymentResponseServerError { reason, .. } => {
392420 assert_eq ! ( reason, "HTTP 503" ) ;
393421 }
394- _ => panic ! ( "expected UpstreamServerError " ) ,
422+ _ => panic ! ( "expected PaymentResponseServerError " ) ,
395423 }
396424 }
397425
@@ -400,10 +428,10 @@ mod tests {
400428 let resp = HttpResponse :: for_test ( 500 , & [ 0xff , 0xfe , 0xfd ] ) ;
401429 let err = parse_payment_rejection ( & resp) ;
402430 match err {
403- PaymentError :: UpstreamServerError { reason, .. } => {
431+ PaymentError :: PaymentResponseServerError { reason, .. } => {
404432 assert_eq ! ( reason, "HTTP 500" ) ;
405433 }
406- _ => panic ! ( "expected UpstreamServerError " ) ,
434+ _ => panic ! ( "expected PaymentResponseServerError " ) ,
407435 }
408436 }
409437
@@ -416,13 +444,19 @@ mod tests {
416444 }
417445
418446 #[ test]
419- fn test_parse_payment_rejection_keeps_payment_proof_invalid_as_payment_rejected ( ) {
447+ fn test_parse_payment_rejection_classifies_non_402_payment_wrapper_as_application_error ( ) {
420448 let resp = HttpResponse :: for_test (
421449 400 ,
422450 br#"{"error":"payment_proof_invalid","message":"invalid payment proof"}"# ,
423451 ) ;
424452 let err = parse_payment_rejection ( & resp) ;
425- assert ! ( matches!( err, PaymentError :: PaymentRejected { .. } ) ) ;
453+ assert ! ( matches!(
454+ err,
455+ PaymentError :: PaymentResponseApplicationError {
456+ status_code: 400 ,
457+ ..
458+ }
459+ ) ) ;
426460 }
427461
428462 #[ test]
0 commit comments