Skip to content

Commit

Permalink
Add Google GenericPass functionality (#14)
Browse files Browse the repository at this point in the history
* Add GroupingInfo->groupingId as per docs
Ref: https://developers.google.com/wallet/generic/rest/v1/GroupingInfo

* - create AbstractClass from BaseClass containing common properties
- create AbstractObject from BaseObject containing common properties

* - add SecurityAnimation class

* - add GenericClass and GenericObject
- add GenericClassesResponse and GenericObjectsResponse
- add GenericClassRepository and GenericObjectRepository
- adjust JWT to include components

* - GenericObject properties are added
- Repositories method signatures updated

* - remove required properties, only one of them can be passed only

* - BarcodeRenderEncoding add legacy values

* - unit test for GenericClass and GenericObject

* - add RotatingBarCode to AbstractObject (incl. all required params
- add ViewUnlockRequirement for AbstractClass

* - add examples + adjust README.md

* - add tests for ViewUnlockRequirement and TotpDetails

* - added LegacyValueCaster for $renderEncoding in Barcode and RotatingBarcode classes / backward compatibility
- remove LegacyValueCaster from SecurityAnimation->animationType

---------

Co-authored-by: Attila Keresztesi <[email protected]>
  • Loading branch information
kerattila and attilaendava authored Feb 20, 2023
1 parent aa588f7 commit 7f27ba2
Show file tree
Hide file tree
Showing 39 changed files with 1,347 additions and 211 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@ composer require chiiya/passes

First, acquire the necessary credentials and certificates as described in [documentation/requirements.md](documentation/requirements.md).

### Apple
For an example, check out [examples/apple.php](examples/apple.php).
### Apple Examples

### Google
For an example, check out [examples/google.php](examples/google.php).
- [examples/apple_coupon.php](examples/apple_coupon.php)

### Google Examples

- [examples/google_offer.php](examples/google_offer.php)
- [examples/google_generic_pass.php](examples/google_generic_pass.php)

## Testing

Expand Down
File renamed without changes.
98 changes: 98 additions & 0 deletions examples/google_generic.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<?php

use Chiiya\Passes\Google\Components\Common\Barcode;
use Chiiya\Passes\Google\Components\Common\DateTime;
use Chiiya\Passes\Google\Components\Common\GroupingInfo;
use Chiiya\Passes\Google\Components\Common\Image;
use Chiiya\Passes\Google\Components\Common\ImageModuleData;
use Chiiya\Passes\Google\Components\Common\LinksModuleData;
use Chiiya\Passes\Google\Components\Common\LocalizedString;
use Chiiya\Passes\Google\Components\Common\TextModuleData;
use Chiiya\Passes\Google\Components\Common\TimeInterval;
use Chiiya\Passes\Google\Components\Common\Uri;
use Chiiya\Passes\Google\Components\Generic\Notifications;
use Chiiya\Passes\Google\Components\Generic\UpcomingNotification;
use Chiiya\Passes\Google\Enumerators\BarcodeRenderEncoding;
use Chiiya\Passes\Google\Enumerators\BarcodeType;
use Chiiya\Passes\Google\Enumerators\MultipleDevicesAndHoldersAllowedStatus;
use Chiiya\Passes\Google\Enumerators\State;
use Chiiya\Passes\Google\Http\GoogleClient;
use Chiiya\Passes\Google\JWT;
use Chiiya\Passes\Google\Passes\GenericClass;
use Chiiya\Passes\Google\Passes\GenericObject;
use Chiiya\Passes\Google\Repositories\GenericClassRepository;
use Chiiya\Passes\Google\ServiceCredentials;

$credentials = ServiceCredentials::parse('service_credentials.json');
$client = GoogleClient::createAuthenticatedClient($credentials);
$repository = new GenericClassRepository($client);

$class = new GenericClass(
id: '1234567890123456789.generic-object',
multipleDevicesAndHoldersAllowedStatus: MultipleDevicesAndHoldersAllowedStatus::MULTIPLE_HOLDERS,
linksModuleData: new LinksModuleData(
uris: [
new Uri(uri: 'https://example.org/app', description: 'App'),
new Uri(uri: 'https://example.org', description: 'Homepage'),
]
),
imageModulesData: [
new ImageModuleData(
mainImage: Image::make('https://example.org/image.png')
)
],
textModulesData: [
new TextModuleData(
header: 'Lorem ipsum',
body: 'Dolor sit amet'
)
]
);
$repository->create($class);

$object = new GenericObject(
classId: '1234567890123456789.generic-object',
id: '1234567890123456789.'.Str::uuid()->toString(),
cardTitle: LocalizedString::make('en', '::cardTitle::'),
subheader: LocalizedString::make('en', '::subheader::'),
header: LocalizedString::make('en', '::header::'),
logo: Image::make('https://example.org/logo.png'),
heroImage: Image::make('https://example.org/hero-image.png'),
hexBackgroundColor: '#333',
state: State::ACTIVE,
barcode: new Barcode(
type: BarcodeType::QR_CODE,
renderEncoding: BarcodeRenderEncoding::UTF_8,
value: '1464194291627',
),
validTimeInterval: new TimeInterval(
start: new DateTime(date: now()),
end: new DateTime(now()->addMonth())
),
notifications: new Notifications(
upcomingNotification: new UpcomingNotification(
enableNotification: true
),
),
textModulesData: [
new TextModuleData(
id: 'key-1',
header: 'label-1',
body: 'value-1',
),
new TextModuleData(
id: 'key-2',
header: 'label-2',
body: 'value-2',
)
],
groupingInfo: new GroupingInfo(
groupingId: 'group1'
)
);

$jwt = (new JWT([
'iss' => $credentials->client_email,
'key' => $credentials->private_key,
'origins' => ['https://example.org'],
]))->addOfferObject($object)->sign();
File renamed without changes.
1 change: 1 addition & 0 deletions src/Google/Components/Common/Barcode.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Barcode extends Component
* Otherwise best known encoding is chosen by Google.
*/
#[ValueIn([BarcodeRenderEncoding::UTF_8, BarcodeRenderEncoding::RENDER_ENCODING_UNSPECIFIED])]
#[CastWith(LegacyValueCaster::class, BarcodeRenderEncoding::class)]
public ?string $renderEncoding;

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Google/Components/Common/GroupingInfo.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class GroupingInfo extends Component
public ?int $sortIndex;

/**
* Optional.
* Optional
* Optional grouping ID for grouping the passes with the same ID visually together. Grouping with different
* types of passes is allowed.
*/
Expand Down
78 changes: 78 additions & 0 deletions src/Google/Components/Common/RotatingBarcode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Common;

use Chiiya\Passes\Common\Casters\LegacyValueCaster;
use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;
use Chiiya\Passes\Common\Validation\ValueIn;
use Chiiya\Passes\Google\Enumerators\BarcodeRenderEncoding;
use Chiiya\Passes\Google\Enumerators\BarcodeType;
use Spatie\DataTransferObject\Attributes\CastWith;

class RotatingBarcode extends Component
{
/**
* Required
* The type of barcode.
*/
#[Required]
#[ValueIn([
BarcodeType::BARCODE_TYPE_UNSPECIFIED,
BarcodeType::AZTEC,
BarcodeType::CODE_39,
BarcodeType::CODE_128,
BarcodeType::CODABAR,
BarcodeType::DATA_MATRIX,
BarcodeType::EAN_8,
BarcodeType::EAN_13,
BarcodeType::ITF_14,
BarcodeType::PDF_417,
BarcodeType::QR_CODE,
BarcodeType::UPC_A,
BarcodeType::TEXT_ONLY,
])]
#[CastWith(LegacyValueCaster::class, BarcodeType::class)]
public ?string $type;

/**
* Optional
* The render encoding for the barcode. When specified, barcode is rendered in the given encoding.
* Otherwise best known encoding is chosen by Google.
*/
#[ValueIn([BarcodeRenderEncoding::UTF_8, BarcodeRenderEncoding::RENDER_ENCODING_UNSPECIFIED])]
#[CastWith(LegacyValueCaster::class, BarcodeRenderEncoding::class)]
public ?string $renderEncoding;

/**
* Required.
* String encoded barcode value.
* This string supports the following substitutions:
* {totp_value_n}: Replaced with the TOTP value (see TotpDetails.parameters).
* {totp_timestamp_millis}: Replaced with the timestamp (millis since epoch) at which the barcode was generated.
* {totp_timestamp_seconds}: Replaced with the timestamp (seconds since epoch) at which the barcode was generated.
*/
#[Required]
public ?string $valuePattern;

/**
* Required
* Details used to evaluate the {totp_value_n} substitutions.
*/
#[Required]
public ?TotpDetails $totpDetails;

/**
* Optional.
* An optional text that will override the default text that shows under the barcode. This field is intended
* for a human readable equivalent of the barcode value, used when the barcode cannot be scanned.
*/
public ?string $alternateText;

/**
* Optional.
* Optional text that will be shown when the barcode is hidden behind a click action. This happens in cases
* where a pass has Smart Tap enabled. If not specified, a default is chosen by Google.
*/
public ?LocalizedString $showCodeText;
}
22 changes: 22 additions & 0 deletions src/Google/Components/Common/SecurityAnimation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Chiiya\Passes\Google\Components\Common;

use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;
use Chiiya\Passes\Common\Validation\ValueIn;
use Chiiya\Passes\Google\Enumerators\AnimationType;

class SecurityAnimation extends Component
{
/**
* Required.
* Type of animation.
*/
#[Required]
#[ValueIn([
AnimationType::ANIMATION_UNSPECIFIED,
AnimationType::FOIL_SHIMMER
])]
public ?string $animationType;
}
37 changes: 37 additions & 0 deletions src/Google/Components/Common/TotpDetails.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Common;

use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;
use Chiiya\Passes\Common\Validation\ValueIn;
use Chiiya\Passes\Google\Enumerators\TotpAlgorithm;
use Spatie\DataTransferObject\Attributes\CastWith;
use Spatie\DataTransferObject\Casters\ArrayCaster;

class TotpDetails extends Component
{
/**
* Required.
* The time interval used for the TOTP value generation, in milliseconds.
*/
#[Required]
public ?string $periodMillis;

/**
* Required.
* The TOTP algorithm used to generate the OTP.
*/
#[Required]
#[ValueIn([TotpAlgorithm::TOTP_ALGORITHM_UNSPECIFIED, TotpAlgorithm::TOTP_SHA1])]
public ?string $algorithm;

/**
* Required.
* The TOTP parameters for each of the {totp_value_*} substitutions.
* The TotpParameters at index n is used for the {totp_value_n} substitution.
*/
#[Required]
#[CastWith(ArrayCaster::class, TotpParameters::class)]
public ?array $parameters;
}
32 changes: 32 additions & 0 deletions src/Google/Components/Common/TotpParameters.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Common;

use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;

class TotpParameters extends Component
{
/**
* Required.
* The secret key used for the TOTP value generation, encoded as a Base16 string.
*/
#[Required]
public ?string $key;

/**
* Required.
* The length of the TOTP value in decimal digits.
*/
#[Required]
public ?int $valueLength;

/**
* Helper method for creating new localized string, eg:
* TotpParameters::make('key', 123).
*/
public static function make(string $key, int $valueLength): static
{
return new static(key: $key, valueLength: $valueLength);
}
}
16 changes: 16 additions & 0 deletions src/Google/Components/Generic/AbstractNotificationValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Generic;

use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;

abstract class AbstractNotificationValue extends Component
{
/**
* Required.
* Indicates that the issuer would like GooglePay to send notifications.
*/
#[Required]
public ?bool $enableNotification;
}
10 changes: 10 additions & 0 deletions src/Google/Components/Generic/ExpiryNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Generic;

class ExpiryNotification extends AbstractNotificationValue
{
/**
* Indicates that the issuer would like GooglePay to send expiry notifications 2 days prior to the card expiration.
*/
}
19 changes: 19 additions & 0 deletions src/Google/Components/Generic/Notifications.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Generic;

use Chiiya\Passes\Common\Component;
use Chiiya\Passes\Common\Validation\Required;

/**
* Indicates if the object needs to have notification enabled.
* We support only one of ExpiryNotification/UpcomingNotification.
* expiryNotification takes precedence over upcomingNotification.
* In other words if expiryNotification is set, we ignore the upcomingNotification field.
*/
class Notifications extends Component
{
public ?ExpiryNotification $expiryNotification;

public ?UpcomingNotification $upcomingNotification;
}
11 changes: 11 additions & 0 deletions src/Google/Components/Generic/UpcomingNotification.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Components\Generic;

class UpcomingNotification extends AbstractNotificationValue
{
/**
* Indicates that the issuer would like GooglePay send an upcoming card validity notification 1 day before
* card becomes valid/usable.
*/
}
20 changes: 20 additions & 0 deletions src/Google/Enumerators/AnimationType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php declare(strict_types=1);

namespace Chiiya\Passes\Google\Enumerators;

final class AnimationType
{
/** @var string */
public const ANIMATION_UNSPECIFIED = 'ANIMATION_UNSPECIFIED';

/** @var string */
public const FOIL_SHIMMER = 'FOIL_SHIMMER';

public static function values(): array
{
return [
self::ANIMATION_UNSPECIFIED,
self::FOIL_SHIMMER
];
}
}
Loading

0 comments on commit 7f27ba2

Please sign in to comment.