added paynym response objects and refactored paynym.is api

This commit is contained in:
julian 2023-01-02 15:16:01 -06:00
parent f9491f8215
commit f3b1d11a46
10 changed files with 299 additions and 81 deletions

View file

@ -0,0 +1,20 @@
class PaynymClaim {
final String claimed;
final String token;
PaynymClaim(this.claimed, this.token);
PaynymClaim.fromMap(Map<String, dynamic> map)
: claimed = map["claimed"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"claimed": claimed,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,23 @@
class PaynymFollow {
final String follower;
final String following;
final String token;
PaynymFollow(this.follower, this.following, this.token);
PaynymFollow.fromMap(Map<String, dynamic> map)
: follower = map["follower"] as String,
following = map["following"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"follower": follower,
"following": following,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -0,0 +1,7 @@
class PaynymResponse<T> {
final T? value;
final int statusCode;
final String message;
PaynymResponse(this.value, this.statusCode, this.message);
}

View file

@ -0,0 +1,23 @@
class PaynymUnfollow {
final String follower;
final String unfollowing;
final String token;
PaynymUnfollow(this.follower, this.unfollowing, this.token);
PaynymUnfollow.fromMap(Map<String, dynamic> map)
: follower = map["follower"] as String,
unfollowing = map["unfollowing"] as String,
token = map["token"] as String;
Map<String, dynamic> toMap() => {
"follower": follower,
"unfollowing": unfollowing,
"token": token,
};
@override
String toString() {
return toMap().toString();
}
}

View file

@ -70,7 +70,7 @@ class _AddNewPaynymFollowViewState
}
setState(() {
_searchResult = paynymAccount;
_searchResult = paynymAccount.value;
});
}
}

View file

@ -108,7 +108,7 @@ class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
debugPrint("created:$created");
if (created.claimed) {
if (created.value!.claimed) {
// payment code already claimed
debugPrint("pcode already claimed!!");
if (mounted) {
@ -126,18 +126,19 @@ class _PaynymClaimViewState extends ConsumerState<PaynymClaimView> {
// sign token with notification private key
final signature =
await wallet.signStringWithNotificationKey(token);
await wallet.signStringWithNotificationKey(token.value!);
// claim paynym account
final claim =
await ref.read(paynymAPIProvider).claim(token, signature);
final claim = await ref
.read(paynymAPIProvider)
.claim(token.value!, signature);
if (claim["claimed"] == pCode.toString()) {
if (claim.value?.claimed == pCode.toString()) {
final account =
await ref.read(paynymAPIProvider).nym(pCode.toString());
ref.read(myPaynymAccountStateProvider.state).state =
account!;
account.value!;
if (mounted) {
Navigator.of(context).popUntil(
ModalRoute.withName(

View file

@ -3,6 +3,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
import 'package:stackwallet/models/paynym/paynym_response.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/paynym/paynym_home_view.dart';
import 'package:stackwallet/pages/paynym/subwidgets/paynym_bot.dart';
@ -127,27 +128,27 @@ class _PaynymFollowToggleButtonState
final myPCode = await wallet.getPaymentCode();
String token = await ref.read(paynymAPIProvider).token(myPCode.toString());
PaynymResponse<String> token =
await ref.read(paynymAPIProvider).token(myPCode.toString());
// sign token with notification private key
String signature = await wallet.signStringWithNotificationKey(token);
String signature = await wallet.signStringWithNotificationKey(token.value!);
var result = await ref
.read(paynymAPIProvider)
.follow(token, signature, followedAccount!.codes.first.code);
var result = await ref.read(paynymAPIProvider).follow(
token.value!, signature, followedAccount.value!.codes.first.code);
int i = 0;
for (;
i < 10 && result["message"] == "401 Unauthorized - Bad signature";
i < 10 &&
result.statusCode == 401; //"401 Unauthorized - Bad signature";
i++) {
token = await ref.read(paynymAPIProvider).token(myPCode.toString());
// sign token with notification private key
signature = await wallet.signStringWithNotificationKey(token);
signature = await wallet.signStringWithNotificationKey(token.value!);
result = await ref
.read(paynymAPIProvider)
.follow(token, signature, followedAccount!.codes.first.code);
result = await ref.read(paynymAPIProvider).follow(
token.value!, signature, followedAccount.value!.codes.first.code);
await Future<void>.delayed(const Duration(milliseconds: 200));
print("RRR result: $result");
@ -155,7 +156,7 @@ class _PaynymFollowToggleButtonState
print("Follow result: $result on try $i");
if (result["following"] == followedAccount!.nymID) {
if (result.value!.following == followedAccount.value!.nymID) {
if (!loadingPopped && mounted) {
Navigator.of(context).pop();
}
@ -163,16 +164,16 @@ class _PaynymFollowToggleButtonState
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "You are following ${followedAccount.nymName}",
message: "You are following ${followedAccount.value!.nymName}",
context: context,
),
);
ref.read(myPaynymAccountStateProvider.state).state!.following.add(
PaynymAccountLite(
followedAccount.nymID,
followedAccount.nymName,
followedAccount.codes.first.code,
followedAccount.codes.first.segwit,
followedAccount.value!.nymID,
followedAccount.value!.nymName,
followedAccount.value!.codes.first.code,
followedAccount.value!.codes.first.segwit,
),
);
@ -189,7 +190,7 @@ class _PaynymFollowToggleButtonState
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Failed to follow ${followedAccount.nymName}",
message: "Failed to follow ${followedAccount.value!.nymName}",
context: context,
),
);
@ -222,34 +223,34 @@ class _PaynymFollowToggleButtonState
final myPCode = await wallet.getPaymentCode();
String token = await ref.read(paynymAPIProvider).token(myPCode.toString());
PaynymResponse<String> token =
await ref.read(paynymAPIProvider).token(myPCode.toString());
// sign token with notification private key
String signature = await wallet.signStringWithNotificationKey(token);
String signature = await wallet.signStringWithNotificationKey(token.value!);
var result = await ref
.read(paynymAPIProvider)
.follow(token, signature, followedAccount!.codes.first.code);
var result = await ref.read(paynymAPIProvider).unfollow(
token.value!, signature, followedAccount.value!.codes.first.code);
int i = 0;
for (;
i < 10 && result["message"] == "401 Unauthorized - Bad signature";
i < 10 &&
result.statusCode == 401; //"401 Unauthorized - Bad signature";
i++) {
token = await ref.read(paynymAPIProvider).token(myPCode.toString());
// sign token with notification private key
signature = await wallet.signStringWithNotificationKey(token);
signature = await wallet.signStringWithNotificationKey(token.value!);
result = await ref
.read(paynymAPIProvider)
.unfollow(token, signature, followedAccount!.codes.first.code);
result = await ref.read(paynymAPIProvider).unfollow(
token.value!, signature, followedAccount.value!.codes.first.code);
await Future<void>.delayed(const Duration(milliseconds: 200));
print("RRR result: $result");
print("unfollow RRR result: $result");
}
print("Unfollow result: $result on try $i");
if (result["unfollowing"] == followedAccount!.nymID) {
if (result.value!.unfollowing == followedAccount.value!.nymID) {
if (!loadingPopped && mounted) {
Navigator.of(context).pop();
}
@ -257,7 +258,7 @@ class _PaynymFollowToggleButtonState
unawaited(
showFloatingFlushBar(
type: FlushBarType.success,
message: "You have unfollowed ${followedAccount.nymName}",
message: "You have unfollowed ${followedAccount.value!.nymName}",
context: context,
),
);
@ -265,7 +266,7 @@ class _PaynymFollowToggleButtonState
.read(myPaynymAccountStateProvider.state)
.state!
.following
.removeWhere((e) => e.nymId == followedAccount.nymID);
.removeWhere((e) => e.nymId == followedAccount.value!.nymID);
setState(() {
isFollowing = false;
@ -280,7 +281,7 @@ class _PaynymFollowToggleButtonState
unawaited(
showFloatingFlushBar(
type: FlushBarType.warning,
message: "Failed to unfollow ${followedAccount.nymName}",
message: "Failed to unfollow ${followedAccount.value!.nymName}",
context: context,
),
);

View file

@ -134,9 +134,10 @@ class _WalletNavigationBarState extends State<WalletNavigationBar> {
Navigator.of(context).pop();
// check if account exists and for matching code to see if claimed
if (account != null && account.codes.first.claimed) {
if (account.value != null &&
account.value!.codes.first.claimed) {
ref.read(myPaynymAccountStateProvider.state).state =
account;
account.value!;
await Navigator.of(context).pushNamed(
PaynymHomeView.routeName,

View file

@ -1,4 +1,4 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/utilities/paynym_api.dart';
import 'package:stackwallet/utilities/paynym_is_api.dart';
final paynymAPIProvider = Provider<PaynymAPI>((_) => PaynymAPI());
final paynymAPIProvider = Provider<PaynymIsApi>((_) => PaynymIsApi());

View file

@ -4,12 +4,19 @@ import 'package:flutter/cupertino.dart';
import 'package:http/http.dart' as http;
import 'package:stackwallet/models/paynym/created_paynym.dart';
import 'package:stackwallet/models/paynym/paynym_account.dart';
import 'package:stackwallet/models/paynym/paynym_claim.dart';
import 'package:stackwallet/models/paynym/paynym_follow.dart';
import 'package:stackwallet/models/paynym/paynym_response.dart';
import 'package:stackwallet/models/paynym/paynym_unfollow.dart';
import 'package:tuple/tuple.dart';
class PaynymAPI {
// todo: better error message parsing (from response itself?)
class PaynymIsApi {
static const String baseURL = "https://paynym.is/api";
static const String version = "/v1";
Future<Map<String, dynamic>> _post(
Future<Tuple2<Map<String, dynamic>, int>> _post(
String endpoint,
Map<String, dynamic> body, [
Map<String, String> additionalHeaders = const {},
@ -29,7 +36,10 @@ class PaynymAPI {
debugPrint("Paynym response code: ${response.statusCode}");
debugPrint("Paynym response body: ${response.body}");
return jsonDecode(response.body) as Map<String, dynamic>;
return Tuple2(
jsonDecode(response.body) as Map<String, dynamic>,
response.statusCode,
);
}
// ### `/api/v1/create`
@ -77,9 +87,28 @@ class PaynymAPI {
//
//
// ------
Future<CreatedPaynym> create(String code) async {
final map = await _post("/create", {"code": code});
return CreatedPaynym.fromMap(map);
Future<PaynymResponse<CreatedPaynym>> create(String code) async {
final result = await _post("/create", {"code": code});
String message;
CreatedPaynym? value;
switch (result.item2) {
case 201:
message = "PayNym created successfully";
value = CreatedPaynym.fromMap(result.item1);
break;
case 200:
message = "PayNym already exists";
value = CreatedPaynym.fromMap(result.item1);
break;
case 400:
message = "Bad request";
break;
default:
message = "Unknown error";
}
return PaynymResponse(value, result.item2, message);
}
// ### `/api/v1/token`
@ -120,9 +149,27 @@ class PaynymAPI {
//
//
// ------
Future<String> token(String code) async {
final map = await _post("/token", {"code": code});
return map["token"] as String;
Future<PaynymResponse<String>> token(String code) async {
final result = await _post("/token", {"code": code});
String message;
String? value;
switch (result.item2) {
case 200:
message = "Token was successfully updated";
value = result.item1["token"] as String;
break;
case 404:
message = "Payment code was not found";
break;
case 400:
message = "Bad request";
break;
default:
message = "Unknown error";
}
return PaynymResponse(value, result.item2, message);
}
// ### `/api/v1/nym`
@ -175,17 +222,43 @@ class PaynymAPI {
// | 200 | Nym found and returned |
// | 404 | Nym not found |
// | 400 | Bad request |
Future<PaynymAccount?> nym(String code, [bool compact = false]) async {
Future<PaynymResponse<PaynymAccount>> nym(String code,
[bool compact = false]) async {
final Map<String, dynamic> requestBody = {"nym": code};
if (compact) {
requestBody["compact"] = true;
}
String message;
PaynymAccount? value;
int statusCode;
try {
final map = await _post("/nym", requestBody);
return PaynymAccount.fromMap(map);
} catch (_) {
return null;
final result = await _post("/nym", requestBody);
statusCode = result.item2;
switch (result.item2) {
case 200:
message = "Nym found and returned";
value = PaynymAccount.fromMap(result.item1);
break;
case 404:
message = "Nym not found";
break;
case 400:
message = "Bad request";
break;
default:
message = "Unknown error";
statusCode = -1;
}
} catch (e) {
value = null;
message = e.toString();
statusCode = -1;
}
return PaynymResponse(value, statusCode, message);
}
// ## Authenticated Requests
@ -238,8 +311,31 @@ class PaynymAPI {
// | 400 | Bad request |
//
// ------
Future<Map<String, dynamic>> claim(String token, String signature) async {
return _post("/claim", {"signature": signature}, {"auth-token": token});
Future<PaynymResponse<PaynymClaim>> claim(
String token,
String signature,
) async {
final result = await _post(
"/claim",
{"signature": signature},
{"auth-token": token},
);
String message;
PaynymClaim? value;
switch (result.item2) {
case 200:
message = "Payment code successfully claimed";
value = PaynymClaim.fromMap(result.item1);
break;
case 400:
message = "Bad request";
break;
default:
message = "Unknown error";
}
return PaynymResponse(value, result.item2, message);
}
// ### `/api/v1/follow`
@ -284,12 +380,12 @@ class PaynymAPI {
// | 401 | Unauthorized token or signature or Unclaimed payment code |
//
// ------
Future<Map<String, dynamic>> follow(
Future<PaynymResponse<PaynymFollow>> follow(
String token,
String signature,
String target,
) async {
return _post(
final result = await _post(
"/follow",
{
"target": target,
@ -299,6 +395,28 @@ class PaynymAPI {
"auth-token": token,
},
);
String message;
PaynymFollow? value;
switch (result.item2) {
case 200:
message = "Added to followers";
value = PaynymFollow.fromMap(result.item1);
break;
case 404:
message = "Payment code not found";
break;
case 400:
message = "Bad request";
break;
case 401:
message = "Unauthorized token or signature or Unclaimed payment code";
break;
default:
message = "Unknown error";
}
return PaynymResponse(value, result.item2, message);
}
// ### `/api/v1/unfollow`
@ -343,12 +461,12 @@ class PaynymAPI {
// | 401 | Unauthorized token or signature or Unclaimed payment code |
//
// ------
Future<Map<String, dynamic>> unfollow(
Future<PaynymResponse<PaynymUnfollow>> unfollow(
String token,
String signature,
String target,
) async {
return _post(
final result = await _post(
"/unfollow",
{
"target": target,
@ -358,6 +476,28 @@ class PaynymAPI {
"auth-token": token,
},
);
String message;
PaynymUnfollow? value;
switch (result.item2) {
case 200:
message = "Unfollowed successfully";
value = PaynymUnfollow.fromMap(result.item1);
break;
case 404:
message = "Payment code not found";
break;
case 400:
message = "Bad request";
break;
case 401:
message = "Unauthorized token or signature or Unclaimed payment code";
break;
default:
message = "Unknown error";
}
return PaynymResponse(value, result.item2, message);
}
// ### `/api/v1/nym/add`
@ -404,22 +544,24 @@ class PaynymAPI {
// | 401 | Unauthorized token or signature or Unclaimed payment code |
//
// ------
Future<Map<String, dynamic>> add(
String token,
String signature,
String nym,
String code,
) async {
return _post(
"/add",
{
"nym": nym,
"code": code,
"signature": signature,
},
{
"auth-token": token,
},
);
}
// NOT USED
// Future<Map<String, dynamic>> add(
// String token,
// String signature,
// String nym,
// String code,
// ) async {
// return _post(
// "/add",
// {
// "nym": nym,
// "code": code,
// "signature": signature,
// },
// {
// "auth-token": token,
// },
// );
// }
}