1313ORIGINAL_TRANSACTION_IDENTIFIER = 1705
1414
1515class ReceiptUtility :
16+ def _decode_octet_string (self , octet_string : bytes ):
17+ decoder = asn1 .Decoder ()
18+ decoder .start (octet_string )
19+ _ , value = decoder .read ()
20+ return value
21+
1622 def extract_transaction_id_from_app_receipt (self , app_receipt : str ) -> Optional [str ]:
1723 """
1824 Extracts a transaction id from an encoded App Receipt. Throws if the receipt does not match the expected format.
@@ -21,68 +27,25 @@ def extract_transaction_id_from_app_receipt(self, app_receipt: str) -> Optional[
2127 :param appReceipt: The unmodified app receipt
2228 :return: A transaction id from the array of in-app purchases, null if the receipt contains no in-app purchases
2329 """
24- decoder = IndefiniteFormAwareDecoder ()
25- decoder .start (b64decode (app_receipt , validate = True ))
26- tag = decoder .peek ()
27- if tag .typ != asn1 .Types .Constructed or tag .nr != asn1 .Numbers .Sequence :
28- raise ValueError ()
29- decoder .enter ()
30- # PKCS#7 object
31- tag , value = decoder .read ()
32- if tag .typ != asn1 .Types .Primitive or tag .nr != asn1 .Numbers .ObjectIdentifier or value != PKCS7_OID :
33- raise ValueError ()
34- # This is the PKCS#7 format, work our way into the inner content
35- decoder .enter ()
36- decoder .enter ()
37- decoder .read ()
38- decoder .read ()
39- decoder .enter ()
40- decoder .read ()
41- decoder .enter ()
42- tag , value = decoder .read ()
43- # Xcode uses nested OctetStrings, we extract the inner string in this case
44- if tag .typ == asn1 .Types .Constructed and tag .nr == asn1 .Numbers .OctetString :
45- inner_decoder = asn1 .Decoder ()
46- inner_decoder .start (value )
47- tag , value = inner_decoder .read ()
48- if tag .typ != asn1 .Types .Primitive or tag .nr != asn1 .Numbers .OctetString :
49- raise ValueError ()
50- decoder = asn1 .Decoder ()
51- decoder .start (value )
52- tag = decoder .peek ()
53- if tag .typ != asn1 .Types .Constructed or tag .nr != asn1 .Numbers .Set :
54- raise ValueError ()
55- decoder .enter ()
56- # We are in the top-level sequence, work our way to the array of in-apps
57- while not decoder .eof ():
58- decoder .enter ()
59- tag , value = decoder .read ()
60- if tag .typ == asn1 .Types .Primitive and tag .nr == asn1 .Numbers .Integer and value == IN_APP_ARRAY :
61- decoder .read ()
62- tag , value = decoder .read ()
63- if tag .typ != asn1 .Types .Primitive or tag .nr != asn1 .Numbers .OctetString :
64- raise ValueError ()
65- inapp_decoder = asn1 .Decoder ()
66- inapp_decoder .start (value )
67- inapp_decoder .enter ()
68- # In-app array
69- while not inapp_decoder .eof ():
70- inapp_decoder .enter ()
71- tag , value = inapp_decoder .read ()
72- if (
73- tag .typ == asn1 .Types .Primitive
74- and tag .nr == asn1 .Numbers .Integer
75- and (value == TRANSACTION_IDENTIFIER or value == ORIGINAL_TRANSACTION_IDENTIFIER )
76- ):
77- inapp_decoder .read ()
78- tag , value = inapp_decoder .read ()
79- singleton_decoder = asn1 .Decoder ()
80- singleton_decoder .start (value )
81- tag , value = singleton_decoder .read ()
82- return value
83- inapp_decoder .leave ()
84- decoder .leave ()
85- return None
30+ try :
31+ val = self ._decode_octet_string (b64decode (app_receipt , validate = True ))
32+ found_oid = val [0 ]
33+ if found_oid != PKCS7_OID :
34+ raise ValueError ()
35+ inner_value = val [1 ][0 ][2 ][1 ][0 ]
36+ # Xcode uses nested OctetStrings, we extract the inner string in this case
37+ value = self ._decode_octet_string (inner_value )
38+ # We are in the top-level sequence, work our way to the array of in-apps
39+ for inner_value in value :
40+ if inner_value [0 ] == IN_APP_ARRAY :
41+ array_values = self ._decode_octet_string (inner_value [2 ])
42+ # In-app array
43+ for array_value in array_values :
44+ if array_value [0 ] == TRANSACTION_IDENTIFIER or array_value [0 ] == ORIGINAL_TRANSACTION_IDENTIFIER :
45+ return self ._decode_octet_string (array_value [2 ])
46+ return None
47+ except Exception as e :
48+ raise ValueError (e )
8649
8750 def extract_transaction_id_from_transaction_receipt (self , transaction_receipt : str ) -> Optional [str ]:
8851 """
@@ -99,19 +62,3 @@ def extract_transaction_id_from_transaction_receipt(self, transaction_receipt: s
9962 if inner_matching_result :
10063 return inner_matching_result .group (1 )
10164 return None
102-
103- class IndefiniteFormAwareDecoder (asn1 .Decoder ):
104- def _read_length (self ) -> int :
105- index , input_data = self .m_stack [- 1 ]
106- try :
107- byte = input_data [index ]
108- except IndexError :
109- raise asn1 .Error ('Premature end of input.' )
110- if byte == 0x80 :
111- # Xcode receipts use indefinite length encoding, not supported by all parsers
112- # Indefinite length encoding is only entered, but never left during parsing for receipts
113- # We therefore round up indefinite length encoding to be the remaining length
114- self ._read_byte ()
115- index , input_data = self .m_stack [- 1 ]
116- return len (input_data ) - index
117- return super ()._read_length ()
0 commit comments