@@ -99,12 +99,34 @@ struct LicensePayloadDecodeTests {
9999 " nonce " : " n-1 " ,
100100 " product " : { " name " : " Pro " , " identifier " : " pro " },
101101 " entitlements " : [],
102- " customer " : { " email " : " buyer@example.com " }
102+ " customer " : { " email " : " buyer@example.com " , " name " : " Amy Worrall " }
103103 }
104104 """
105105 let payload = try decodePayload ( json )
106106 let license = License ( from : payload )
107107 #expect ( license . customer ? . email == " buyer@example.com " )
108+ #expect ( license . customer ? . name == " Amy Worrall " )
109+ }
110+
111+ // A name-only customer (license issued without an email) must decode with
112+ // a nil email and carry the name.
113+ @ Test func customerDecodesWithNameOnly ( ) throws {
114+ let json = """
115+ {
116+ " exp " : 1800000000,
117+ " iat " : 1779000000,
118+ " hardware_id " : " hw-1 " ,
119+ " license_id " : " C7B53B0E-2C18-4F1D-8C9B-2B7B6A8B6A7E " ,
120+ " nonce " : " n-1 " ,
121+ " product " : { " name " : " Pro " , " identifier " : " pro " },
122+ " entitlements " : [],
123+ " customer " : { " name " : " Amy Worrall " }
124+ }
125+ """
126+ let payload = try decodePayload ( json )
127+ let license = License ( from : payload )
128+ #expect ( license . customer ? . name == " Amy Worrall " )
129+ #expect ( license . customer ? . email == nil )
108130 }
109131
110132 // An older token (or any license issued without an email) has no `customer`
@@ -126,6 +148,66 @@ struct LicensePayloadDecodeTests {
126148 #expect ( license . customer == nil )
127149 }
128150
151+ // The full claim set a current server mints: customer name and email plus
152+ // the issue date, all on one token.
153+ @ Test func decodesFullCurrentTokenShape ( ) throws {
154+ let json = """
155+ {
156+ " exp " : 1800000000,
157+ " iat " : 1779000000,
158+ " hardware_id " : " hw-1 " ,
159+ " license_id " : " C7B53B0E-2C18-4F1D-8C9B-2B7B6A8B6A7E " ,
160+ " nonce " : " n-1 " ,
161+ " product " : { " name " : " Pro " , " identifier " : " pro " },
162+ " entitlements " : [],
163+ " customer " : { " email " : " buyer@example.com " , " name " : " Amy Worrall " },
164+ " issued_at " : 1778000000
165+ }
166+ """
167+ let payload = try decodePayload ( json )
168+ let license = License ( from : payload )
169+ #expect ( license . customer ? . email == " buyer@example.com " )
170+ #expect ( license . customer ? . name == " Amy Worrall " )
171+ #expect ( license . issuedAt == Date ( timeIntervalSince1970 : 1_778_000_000 ) )
172+ }
173+
174+ @ Test func licenseFromPayloadCarriesIssueDate ( ) throws {
175+ let json = """
176+ {
177+ " exp " : 1800000000,
178+ " iat " : 1779000000,
179+ " hardware_id " : " hw-1 " ,
180+ " license_id " : " C7B53B0E-2C18-4F1D-8C9B-2B7B6A8B6A7E " ,
181+ " nonce " : " n-1 " ,
182+ " product " : { " name " : " Pro " , " identifier " : " pro " },
183+ " entitlements " : [],
184+ " issued_at " : 1778000000
185+ }
186+ """
187+ let payload = try decodePayload ( json )
188+ let license = License ( from : payload )
189+ #expect ( license . issuedAt == Date ( timeIntervalSince1970 : 1_778_000_000 ) )
190+ }
191+
192+ // An older token has no `issued_at` claim; the optional must decode to
193+ // nil, not error.
194+ @ Test func licenseHasNilIssueDateWhenClaimAbsent ( ) throws {
195+ let json = """
196+ {
197+ " exp " : 1800000000,
198+ " iat " : 1779000000,
199+ " hardware_id " : " hw-1 " ,
200+ " license_id " : " C7B53B0E-2C18-4F1D-8C9B-2B7B6A8B6A7E " ,
201+ " nonce " : " n-1 " ,
202+ " product " : { " name " : " Pro " , " identifier " : " pro " },
203+ " entitlements " : []
204+ }
205+ """
206+ let payload = try decodePayload ( json )
207+ let license = License ( from : payload )
208+ #expect ( license . issuedAt == nil )
209+ }
210+
129211 // A `customer` object may be present without an `email` (partial or
130212 // forward-compatible token). It must decode with a nil email rather than
131213 // throw and reject the entire license.
0 commit comments