Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(passkit_server): Passkit Backend #76

Merged
merged 14 commits into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

This repo contains various packages to deal with PkPass files.

| Package | Description | Version | Likes | Popularity | Points |
|----------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|
| [`app`](app) | App which makes use of the various libraries. Android only. | | | | |
| [`apple_passkit`](apple_passkit) | Package to interface with the native functionality on iOS | [![pub package](https://img.shields.io/pub/v/apple_passkit.svg)](https://pub.dev/packages/apple_passkit) | [![likes](https://img.shields.io/pub/likes/apple_passkit)](https://pub.dev/packages/apple_passkit/score) | [![popularity](https://img.shields.io/pub/popularity/apple_passkit)](https://pub.dev/packages/apple_passkit/score) | [![pub points](https://img.shields.io/pub/points/apple_passkit)](https://pub.dev/packages/apple_passkit/score) |
| [`passkit`](passkit) | Pure Dart package to read PkPass files. Can be used on server side too | [![pub package](https://img.shields.io/pub/v/passkit.svg)](https://pub.dev/packages/passkit) | [![likes](https://img.shields.io/pub/likes/passkit)](https://pub.dev/packages/passkit/score) | [![popularity](https://img.shields.io/pub/popularity/passkit)](https://pub.dev/packages/passkit/score) | [![pub points](https://img.shields.io/pub/points/passkit)](https://pub.dev/packages/passkit/score) |
| [`passkit_ui`](passkit_ui) | Pure Flutter package to show PkPass files. Makes use of `passkit` | [![pub package](https://img.shields.io/pub/v/passkit_ui.svg)](https://pub.dev/packages/passkit_ui) | [![likes](https://img.shields.io/pub/likes/passkit_ui)](https://pub.dev/packages/passkit_ui/score) | [![popularity](https://img.shields.io/pub/popularity/passkit_ui)](https://pub.dev/packages/passkit_ui/score) | [![pub points](https://img.shields.io/pub/points/passkit_ui)](https://pub.dev/packages/passkit_ui/score) |
| Package | Description | Version | Likes | Popularity | Points |
| ---------------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| [`app`](app) | App which makes use of the various libraries. Android only. | | | | |
| [`apple_passkit`](apple_passkit) | Package to interface with the native functionality on iOS | [![pub package](https://img.shields.io/pub/v/apple_passkit.svg)](https://pub.dev/packages/apple_passkit) | [![likes](https://img.shields.io/pub/likes/apple_passkit)](https://pub.dev/packages/apple_passkit/score) | [![popularity](https://img.shields.io/pub/popularity/apple_passkit)](https://pub.dev/packages/apple_passkit/score) | [![pub points](https://img.shields.io/pub/points/apple_passkit)](https://pub.dev/packages/apple_passkit/score) |
| [`passkit_server`](passkit_server) | Dart package to add PassKit enpoints to a shelf server. | [![pub package](https://img.shields.io/pub/v/passkit_server.svg)](https://pub.dev/packages/passkit_server) | [![likes](https://img.shields.io/pub/likes/passkit_server)](https://pub.dev/packages/passkit_server/score) | [![popularity](https://img.shields.io/pub/popularity/passkit_server)](https://pub.dev/packages/passkit_server/score) | [![pub points](https://img.shields.io/pub/points/passkit_server)](https://pub.dev/packages/passkit_server/score) |
| [`passkit_ui`](passkit_ui) | Pure Flutter package to show PkPass files. Makes use of `passkit` | [![pub package](https://img.shields.io/pub/v/passkit_ui.svg)](https://pub.dev/packages/passkit_ui) | [![likes](https://img.shields.io/pub/likes/passkit_ui)](https://pub.dev/packages/passkit_ui/score) | [![popularity](https://img.shields.io/pub/popularity/passkit_ui)](https://pub.dev/packages/passkit_ui/score) | [![pub points](https://img.shields.io/pub/points/passkit_ui)](https://pub.dev/packages/passkit_ui/score) |
| [`passkit`](passkit) | Pure Dart package to read PkPass files. Can be used on server side too | [![pub package](https://img.shields.io/pub/v/passkit.svg)](https://pub.dev/packages/passkit) | [![likes](https://img.shields.io/pub/likes/passkit)](https://pub.dev/packages/passkit/score) | [![popularity](https://img.shields.io/pub/popularity/passkit)](https://pub.dev/packages/passkit/score) | [![pub points](https://img.shields.io/pub/points/passkit)](https://pub.dev/packages/passkit/score) |
7 changes: 7 additions & 0 deletions passkit_server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
3 changes: 3 additions & 0 deletions passkit_server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 1.0.0

- Initial version.
105 changes: 105 additions & 0 deletions passkit_server/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# PassKit Server

[![pub package](https://img.shields.io/pub/v/passkit_server.svg)](https://pub.dev/packages/passkit_server)
[![likes](https://img.shields.io/pub/likes/passkit_server)](https://pub.dev/packages/passkit_server/score)
[![popularity](https://img.shields.io/pub/popularity/passkit_server)](https://pub.dev/packages/passkit_server/score)
[![pub points](https://img.shields.io/pub/points/passkit_server)](https://pub.dev/packages/passkit_server/score)


[![Twitter Follow](https://img.shields.io/twitter/follow/ue_man?style=social)](https://twitter.com/ue_man)
[![GitHub followers](https://img.shields.io/github/followers/ueman?style=social)](https://github.com/ueman)

-------

PassKit allows you to work with Apple's PkPass and Order files. This is a Dart library, which allows you to integrate the PassKit enpoint in your shelf (and potentially dart_frog) application.

In order to show PassKit and Order files in Flutter, use the [`passkit_ui`](https://pub.dev/packages/passkit_ui) package, which includes ready made widgets.

Want to work with Apple's native PassKit APIs in Flutter? Consider using [`apple_passkit`](https://pub.dev/packages/apple_passkit).

Please read through the [Apple Documentation](https://developer.apple.com/documentation/walletpasses/adding-a-web-service-to-update-passes) for the server implementation first.

A brief example looks roughly like this. Unfortunately, it's not possible to move more logic into
the library, since it depends too much on your application logic.

```dart
import 'dart:async';

import 'package:passkit/src/pkpass/pkpass.dart';
import 'package:passkit_server/passkit_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';

Future<void> main() async {
var app = Router();

app.addPassKitServer(CustomPassKitBackend());

app.get('/hello', (Request request) {
return Response.ok('hello-world');
});

var server = await io.serve(app.call, 'localhost', 8888);
}

class CustomPassKitBackend extends PassKitBackend {
@override
Future<PkPass?> getLatestPassFor(String identifier, String serial) async {
print('getLatestPassFor($identifier, $serial)');
return null;
}

@override
FutureOr<bool> isKnownDeviceId(String deviceId) {
print('isKnownDeviceId($deviceId)');
return true;
}

@override
FutureOr<bool> isValidAuthToken(String serial, String authToken) {
print('isValidAuthToken($serial, $authToken)');
return true;
}

@override
Future<void> logMessage(Map<String, dynamic> message) async {
print('logMessage($message)');
}

@override
Future<UpdatablePassResponse?> returnUpdatablePasses(
String deviceId,
String typeId,
String? lastTag,
) async {
print('returnUpdatablePasses($deviceId, $typeId, $lastTag)');
return null;
}

@override
Future<NotificationRegistrationReponse> startSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
String pushToken,
) async {
print(
'startSendingPushNotificationsFor($deviceId, $passTypeId, $serialNumber, $pushToken)',
);
return NotificationRegistrationReponse.created;
}

@override
Future<bool> stopSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
) async {
print(
'stopSendingPushNotificationsFor($deviceId, $passTypeId, $serialNumber)',
);
return true;
}
}
```
27 changes: 27 additions & 0 deletions passkit_server/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
include: package:lints/recommended.yaml

linter:
rules:
prefer_single_quotes: true
unawaited_futures: true
sort_constructors_first: true
use_key_in_widget_constructors: true
use_super_parameters: true
use_colored_box: true
use_decorated_box: true
no_leading_underscores_for_local_identifiers: true
require_trailing_commas: true
flutter_style_todos: true
sort_pub_dependencies: true

analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
errors:
missing_required_param: error
missing_return: error
todo: ignore
exclude:
- "**.g.dart"
79 changes: 79 additions & 0 deletions passkit_server/example/passkit_server_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import 'dart:async';

import 'package:passkit/src/pkpass/pkpass.dart';
import 'package:passkit_server/passkit_server.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';

Future<void> main() async {
var app = Router();

app.addPassKitServer(CustomPassKitBackend());

app.get('/hello', (Request request) {
return Response.ok('hello-world');
});

var server = await io.serve(app.call, 'localhost', 8888);
}

class CustomPassKitBackend extends PassKitBackend {
@override
Future<PkPass?> getLatestPassFor(String identifier, String serial) async {
print('getLatestPassFor($identifier, $serial)');
return null;
}

@override
FutureOr<bool> isKnownDeviceId(String deviceId) {
print('isKnownDeviceId($deviceId)');
return true;
}

@override
FutureOr<bool> isValidAuthToken(String serial, String authToken) {
print('isValidAuthToken($serial, $authToken)');
return true;
}

@override
Future<void> logMessage(Map<String, dynamic> message) async {
print('logMessage($message)');
}

@override
Future<UpdatablePassResponse?> returnUpdatablePasses(
String deviceId,
String typeId,
String? lastTag,
) async {
print('returnUpdatablePasses($deviceId, $typeId, $lastTag)');
return null;
}

@override
Future<NotificationRegistrationReponse> startSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
String pushToken,
) async {
print(
'startSendingPushNotificationsFor($deviceId, $passTypeId, $serialNumber, $pushToken)',
);
return NotificationRegistrationReponse.created;
}

@override
Future<bool> stopSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
) async {
print(
'stopSendingPushNotificationsFor($deviceId, $passTypeId, $serialNumber)',
);
return true;
}
}
2 changes: 2 additions & 0 deletions passkit_server/lib/passkit_server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'src/passkit_backend.dart';
export 'src/passkit_server.dart' show PasskitServerExtension;
68 changes: 68 additions & 0 deletions passkit_server/lib/src/passkit_backend.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import 'dart:async';

import 'package:passkit/passkit.dart';

abstract class PassKitBackend {
/// Saves JSON that gets send to `/v1/log`
Future<void> logMessage(Map<String, dynamic> message);

/// Return `null` if there are no updatable passes.
/// Otherwise return an instance of it.
/// [lastTag], if non-null, describes the last point in time where the wallet
/// app made a request to get updateable passes.
Future<UpdatablePassResponse?> returnUpdatablePasses(
String deviceId,
String typeId,
String? lastTag,
);

/// Must return the latest pass for the given [identifier] and [serial]
Future<PkPass?> getLatestPassFor(
String identifier,
String serial,
);

/// Start sending push notifications for the given parameters.
/// Consider that a user can have added the same pass to multiple devices.
Future<NotificationRegistrationReponse> startSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
String pushToken,
);

/// Stop sending push notifications for the given parameters.
/// Consider that a user can have added the same pass to multiple devices.
Future<bool> stopSendingPushNotificationsFor(
String deviceId,
String passTypeId,
String serialNumber,
);

/// Should return true if the [serial] and [authToken] match each other
/// and are valid on their own. Otherwise it should return false.
FutureOr<bool> isValidAuthToken(String serial, String authToken);

/// Should return true if the [deviceId] is known, otherwise it should return
/// false.
FutureOr<bool> isKnownDeviceId(String deviceId);
}

class UpdatablePassResponse {
UpdatablePassResponse({this.tag, required this.ids}) : assert(ids.isNotEmpty);

final String? tag;
final List<String> ids;

Map<String, dynamic> toJson() {
return {
'lastUpdated': tag,
'serialNumbers': ids,
};
}
}

enum NotificationRegistrationReponse {
created,
existing,
}
Loading
Loading