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

Feature/tta/free products #396

Merged
merged 12 commits into from
Apr 16, 2023
fremartini marked this conversation as resolved.
Show resolved Hide resolved
fremartini marked this conversation as resolved.
Show resolved Hide resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/base/strings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ abstract class Strings {
static const confirmSwipe = 'Confirm use of ticket';
static const confirmPurchase = 'Confirm purchase';
static const tapHereToCancel = 'Tap here to cancel';
static const String paymentConfirmationButtonRedeem = 'Get your free product';

static String paymentConfirmationTopTickets(int amount, String title) {
return "You're buying $amount $title tickets";
Expand All @@ -147,7 +148,7 @@ abstract class Strings {
return "You're buying and swiping $amount $title";
}

static String paymentConfirmationBottom(int price) {
static String paymentConfirmationBottomPurchase(int price) {
return 'Pay $price,- with...';
}

Expand Down
13 changes: 7 additions & 6 deletions lib/cubits/purchase/purchase_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ class PurchaseCubit extends Cubit<PurchaseState> {
PurchaseCubit({required this.paymentHandler, required this.product})
: super(const PurchaseInitial());

Future<void> payWithMobilePay() async {
Future<void> pay() async {
sl<FirebaseAnalyticsEventLogging>().beginCheckoutEvent(product);

if (state is PurchaseInitial) {
sl<FirebaseAnalyticsEventLogging>().beginCheckoutEvent(product);
emit(const PurchaseStarted());

final either = await paymentHandler.initPurchase(product.id);

either.fold(
(error) => emit(PurchaseError(error.reason)),
(payment) async {
if (payment.status != PaymentStatus.error) {
(payment) {
if (payment.status == PaymentStatus.completed) {
emit(PurchaseCompleted(payment));
} else if (payment.status == PaymentStatus.awaitingPayment) {
emit(PurchaseProcessing(payment));
await paymentHandler
.invokePaymentMethod(Uri.parse(payment.deeplink));
} else {
emit(PurchasePaymentRejected(payment));
}
Expand Down
2 changes: 1 addition & 1 deletion lib/cubits/receipt/receipt_cubit.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:coffeecard/base/strings.dart';
import 'package:coffeecard/data/repositories/v1/receipt_repository.dart';
import 'package:coffeecard/data/repositories/v2/receipt_repository.dart';
import 'package:coffeecard/models/receipts/receipt.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand Down
40 changes: 0 additions & 40 deletions lib/data/repositories/v1/receipt_repository.dart
TTA777 marked this conversation as resolved.
Outdated
Show resolved Hide resolved

This file was deleted.

2 changes: 1 addition & 1 deletion lib/data/repositories/v1/ticket_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ class TicketRepository {
),
);

return result.map(Receipt.fromTicketDTO);
return result.map(Receipt.fromTicketDto);
}
}
50 changes: 50 additions & 0 deletions lib/data/repositories/v2/receipt_repository.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import 'package:coffeecard/core/errors/failures.dart';
import 'package:coffeecard/core/network/network_request_executor.dart';
import 'package:coffeecard/data/repositories/v1/product_repository.dart';
import 'package:coffeecard/generated/api/coffeecard_api_v2.swagger.dart';
import 'package:coffeecard/models/receipts/receipt.dart';
import 'package:dartz/dartz.dart';

class ReceiptRepository {
ReceiptRepository({
required this.productRepository,
required this.apiV2,
required this.executor,
});

final CoffeecardApiV2 apiV2;
final ProductRepository productRepository;
final NetworkRequestExecutor executor;

/// Retrieves all of the users receipts
/// This includes both their used tickets and purchased tickets
Future<Either<Failure, List<Receipt>>> getUserReceipts() async {
final usedTicketsFutureEither = executor(
() => apiV2.apiV2TicketsGet(includeUsed: true),
);
final purchasedTicketsFutureEither = executor(
apiV2.apiV2PurchasesGet,
);

final usedTicketsEither = (await usedTicketsFutureEither)
.map((dto) => dto.map(Receipt.fromTicketResponse));
final purchasedTicketsEither = (await purchasedTicketsFutureEither).map(
(r) => r.map(
(purchase) => Receipt.fromSimplePurchaseResponse(
purchase,
),
),
);

return usedTicketsEither.fold(
(l) => Left(l),
(usedTickets) => purchasedTicketsEither.map(
(purchasedTickets) {
final allTickets = [...usedTickets, ...purchasedTickets];
allTickets.sort((a, b) => b.timeUsed.compareTo(a.timeUsed));
return allTickets;
},
),
);
}
}
3 changes: 0 additions & 3 deletions lib/models/purchase/payment.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'package:coffeecard/models/purchase/payment_status.dart';

class Payment {
final int id;
final String paymentId;
final PaymentStatus status;
final String deeplink;
final int price;
Expand All @@ -14,7 +13,6 @@ class Payment {
required this.id,
required this.price,
required this.purchaseTime,
required this.paymentId,
required this.status,
required this.deeplink,
required this.productId,
Expand All @@ -33,7 +31,6 @@ class Payment {
}) {
return Payment(
id: id ?? this.id,
paymentId: paymentId ?? this.paymentId,
status: status ?? this.status,
deeplink: deeplink ?? this.deeplink,
price: price ?? this.price,
Expand Down
24 changes: 21 additions & 3 deletions lib/models/purchase/payment_status.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'package:coffeecard/generated/api/coffeecard_api_v2.enums.swagger.dart';

enum PaymentStatus {
/// Payment is completed
completed,
Expand All @@ -8,11 +10,27 @@ enum PaymentStatus {
/// Payment is not yet complete
reserved,

// User has not approved the purchase
/// User has not approved the purchase
awaitingPayment,

// User has rejected payment
/// User has rejected payment
rejectedPayment,

awaitingCompletionAfterRetry,
/// Payment has been refunded
refunded;

static PaymentStatus fromPurchaseStatus(PurchaseStatus status) {
switch (status) {
case PurchaseStatus.swaggerGeneratedUnknown:
return PaymentStatus.error;
case PurchaseStatus.completed:
return PaymentStatus.completed;
case PurchaseStatus.cancelled:
return PaymentStatus.rejectedPayment;
case PurchaseStatus.pendingpayment:
return PaymentStatus.awaitingPayment;
case PurchaseStatus.refunded:
return PaymentStatus.refunded;
}
}
}
9 changes: 6 additions & 3 deletions lib/models/purchase/single_purchase.dart
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import 'package:coffeecard/generated/api/coffeecard_api_v2.swagger.dart';
import 'package:coffeecard/models/purchase/payment_status.dart';

class SinglePurchase {
final int id;
final int totalAmount;
final Map<String, dynamic> paymentDetails;
final String purchaseStatus;
final PaymentStatus status;
final DateTime dateCreated;

const SinglePurchase({
required this.id,
required this.totalAmount,
required this.paymentDetails,
required this.purchaseStatus,
required this.status,
required this.dateCreated,
});

SinglePurchase.fromDto(SinglePurchaseResponse dto)
: id = dto.id,
totalAmount = dto.totalAmount,
paymentDetails = dto.paymentDetails as Map<String, dynamic>,
purchaseStatus = dto.purchaseStatus as String,
status = PaymentStatus.fromPurchaseStatus(
purchaseStatusFromJson(dto.purchaseStatus),
),
dateCreated = dto.dateCreated;
}
20 changes: 16 additions & 4 deletions lib/models/receipts/receipt.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:coffeecard/generated/api/coffeecard_api.swagger.dart';
import 'package:coffeecard/generated/api/coffeecard_api_v2.models.swagger.dart';

enum TransactionType { purchase, ticketSwipe, placeholder }

Expand All @@ -21,7 +22,7 @@ class Receipt {
});

/// Creates a receipt from a used ticket DTO
Receipt.fromTicketDTO(TicketDto dto)
Receipt.fromTicketDto(TicketDto dto)
: productName = dto.productName,
transactionType = TransactionType.ticketSwipe,
timeUsed = dto
Expand All @@ -30,12 +31,23 @@ class Receipt {
amountPurchased = 1,
id = dto.id;

/// Creates a receipt from a purchase DTO
Receipt.fromPurchaseDTO(PurchaseDto dto)
/// Creates a receipt from a used ticket DTO
Receipt.fromTicketResponse(TicketResponse dto)
: productName = dto.productName,
transactionType = TransactionType.ticketSwipe,
timeUsed = dto
.dateUsed!, // will not be null as the dto is a ticket that has been used at some point
price = 1,
amountPurchased = 1,
id = dto.id;

/// Creates a receipt from a purchase DTO
Receipt.fromSimplePurchaseResponse(
SimplePurchaseResponse dto,
) : productName = dto.productName,
transactionType = TransactionType.purchase,
timeUsed = dto.dateCreated,
price = dto.price,
price = dto.totalAmount,
amountPurchased = dto.numberOfTickets,
id = dto.id;
}
36 changes: 36 additions & 0 deletions lib/payment/free_product_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:coffeecard/core/errors/failures.dart';
import 'package:coffeecard/generated/api/coffeecard_api_v2.enums.swagger.dart';
import 'package:coffeecard/models/purchase/payment.dart';
import 'package:coffeecard/models/purchase/payment_status.dart';
import 'package:coffeecard/payment/payment_handler.dart';
import 'package:dartz/dartz.dart';

class FreeProductService extends PaymentHandler {
const FreeProductService({
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
required super.purchaseRepository,
required super.context,
});

@override
Future<Either<Failure, Payment>> initPurchase(int productId) async {
final response = await purchaseRepository.initiatePurchase(
productId,
PaymentType.freepurchase,
);

return response.fold(
(error) => Left(error),
(purchase) => Right(
Payment(
id: purchase.id,
status: PaymentStatus.completed,
deeplink: '',
TTA777 marked this conversation as resolved.
Show resolved Hide resolved
purchaseTime: purchase.dateCreated,
price: purchase.totalAmount,
productId: purchase.productId,
productName: purchase.productName,
),
),
);
}
}
Loading