@@ -19,8 +19,9 @@ private Parsing() {}
1919
2020 // Matches: key="quoted value" or key=token
2121 private static final Pattern AUTH_PARAM = Pattern .compile (
22- "(\\ w+)= (?:\" ([^\" \\ \\ ]*(?:\\ \\ .[^\" \\ \\ ]*)*)\" |([^\\ s,]+))"
22+ "([A-Za-z_][ \\ w-]*) \\ s*= \\ s* (?:\" ([^\" \\ \\ ]*(?:\\ \\ .[^\" \\ \\ ]*)*)\" |([^\\ s,]+))"
2323 );
24+ private static final Pattern PAYMENT_METHOD_ID = Pattern .compile ("[a-z]+" );
2425
2526 // --- Encoding helpers ---
2627
@@ -31,7 +32,12 @@ static String b64Encode(Object data) {
3132
3233 /** Decode base64url to a parsed JSON object, or return the raw string if not JSON. */
3334 static Object b64Decode (String encoded ) {
34- byte [] bytes = ChallengeId .b64urlDecode (encoded );
35+ byte [] bytes ;
36+ try {
37+ bytes = ChallengeId .b64urlDecode (encoded );
38+ } catch (IllegalArgumentException e ) {
39+ throw new ParseException ("Invalid base64url encoding" , e );
40+ }
3541 String str = new String (bytes , StandardCharsets .UTF_8 );
3642 try {
3743 return Json .parse (str );
@@ -54,6 +60,7 @@ static Map<String, String> parseAuthParams(String input) {
5460 Matcher m = AUTH_PARAM .matcher (input );
5561 while (m .find ()) {
5662 String key = m .group (1 );
63+ if (params .containsKey (key )) throw new ParseException ("Duplicate parameter: " + key );
5764 String value = m .group (2 ) != null ? m .group (2 ) : m .group (3 );
5865 // Unescape backslash sequences in quoted strings
5966 if (m .group (2 ) != null ) value = value .replace ("\\ \" " , "\" " ).replace ("\\ \\ " , "\\ " );
@@ -62,6 +69,20 @@ static Map<String, String> parseAuthParams(String input) {
6269 return params ;
6370 }
6471
72+ static String requireString (Map <String , ?> map , String key ) {
73+ Object value = map .get (key );
74+ if (!(value instanceof String ) || ((String ) value ).isEmpty ()) {
75+ throw new ParseException ("Missing " + key );
76+ }
77+ return (String ) value ;
78+ }
79+
80+ static void validatePaymentMethodId (String method ) {
81+ if (method == null || !PAYMENT_METHOD_ID .matcher (method ).matches ()) {
82+ throw new ParseException ("Invalid payment method ID" );
83+ }
84+ }
85+
6586 private static String quote (String value ) {
6687 if (value .indexOf ('\r' ) >= 0 || value .indexOf ('\n' ) >= 0 ) {
6788 throw new IllegalArgumentException ("Header values must not contain CR or LF" );
@@ -98,6 +119,8 @@ static Credential parseAuthorization(String header) {
98119 Map <String , Object > challengeMap = (Map <String , Object >) challengeObj ;
99120
100121 if (!challengeMap .containsKey ("id" )) throw new ParseException ("Credential challenge missing required field: id" );
122+ String method = requireString (challengeMap , "method" );
123+ validatePaymentMethodId (method );
101124
102125 Map <String , Object > opaque = null ;
103126 if (challengeMap .get ("opaque" ) instanceof Map ) {
@@ -107,7 +130,7 @@ static Credential parseAuthorization(String header) {
107130 ChallengeEcho echo = new ChallengeEcho (
108131 str (challengeMap , "id" ),
109132 str (challengeMap , "realm" ),
110- str ( challengeMap , " method" ) ,
133+ method ,
111134 str (challengeMap , "intent" ),
112135 str (challengeMap , "request" ),
113136 str (challengeMap , "expires" ),
@@ -118,7 +141,7 @@ static Credential parseAuthorization(String header) {
118141 Object payload = envelope .get ("payload" );
119142 if (payload == null ) throw new ParseException ("Credential missing required field: payload" );
120143
121- String source = envelope .get ("source" ) instanceof String ? (String ) envelope .get ("source" ) : header ;
144+ String source = envelope .get ("source" ) instanceof String ? (String ) envelope .get ("source" ) : null ;
122145 return new Credential (echo , payload , source );
123146 }
124147
@@ -170,12 +193,14 @@ static Receipt parsePaymentReceipt(String header) {
170193 String reference = str (map , "reference" );
171194 if (reference == null ) throw new ParseException ("Missing reference" );
172195
173- String method = str (map , "method" );
174- if (method == null ) method = "" ;
196+ String method = requireString (map , "method" );
197+ validatePaymentMethodId (method ) ;
175198
176199 Object extra = map .get ("extra" );
177200
178- return new Receipt (status , timestamp , reference , method , str (map , "external_id" ), extra );
201+ String externalId = str (map , "externalId" );
202+ if (externalId == null ) externalId = str (map , "external_id" );
203+ return new Receipt (status , timestamp , reference , method , externalId , extra );
179204 }
180205
181206 static String formatPaymentReceipt (Receipt receipt ) {
@@ -186,7 +211,7 @@ static String formatPaymentReceipt(Receipt receipt) {
186211 if (receipt .method () != null && !receipt .method ().isEmpty ())
187212 map .put ("method" , receipt .method ());
188213 if (receipt .externalId () != null )
189- map .put ("external_id " , receipt .externalId ());
214+ map .put ("externalId " , receipt .externalId ());
190215 if (receipt .extra () != null )
191216 map .put ("extra" , receipt .extra ());
192217 return b64Encode (map );
0 commit comments