Skip to content

Commit 5e56ca2

Browse files
authored
Merge pull request #10 from lightsparkdev/feat/umainvites
Add the UMA invites functions.
2 parents 92c437e + 2cb6b51 commit 5e56ca2

23 files changed

Lines changed: 1302 additions & 2 deletions

examples/example_uma_invites.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Copyright ©, 2022-present, Lightspark Group, Inc. - All Rights Reserved
2+
3+
import logging
4+
import os
5+
6+
import lightspark
7+
8+
logger = logging.getLogger("uma_invites")
9+
logger.setLevel(logging.DEBUG)
10+
11+
#################################################################
12+
## MODIFY THOSE VARIABLES BEFORE RUNNING THE EXAMPLE
13+
#################################################################
14+
##
15+
## We defined those variables as environment variables, but if you are just
16+
## running the example locally, feel free to just set the values in Python.
17+
##
18+
19+
api_token_id = os.environ.get("LIGHTSPARK_API_TOKEN_CLIENT_ID")
20+
api_token_secret = os.environ.get("LIGHTSPARK_API_TOKEN_CLIENT_SECRET")
21+
base_url = os.environ.get("LIGHTSPARK_EXAMPLE_BASE_URL")
22+
23+
# Let's start by creating a client
24+
25+
assert api_token_secret
26+
assert api_token_id
27+
28+
client = lightspark.LightsparkSyncClient(
29+
api_token_client_id=api_token_id,
30+
api_token_client_secret=api_token_secret,
31+
base_url=base_url,
32+
)
33+
34+
# Create an invitation
35+
invitation = client.create_uma_invitation_with_incentives(
36+
inviter_uma="$alice@testvasp1.com",
37+
inviter_phone_number_e164="+11234567890",
38+
inviter_region=lightspark.RegionCode.US,
39+
)
40+
41+
print(
42+
f"Created an invitation with code={invitation.code}, url={invitation.url}, and incentives status={invitation.incentives_status.name}"
43+
)
44+
45+
46+
# Claim an invitation
47+
48+
client.claim_uma_invitation_with_incentives(
49+
invitation_code=invitation.code,
50+
invitee_uma="$bob@testvasp2.com",
51+
invitee_phone_number_e164="+520987654321",
52+
invitee_region=lightspark.RegionCode.MX,
53+
)
54+
55+
print("Claimed invitation!")
56+
57+
58+
# Claiming an invitation again!
59+
60+
try:
61+
print("Claiming the same invitation again...")
62+
client.claim_uma_invitation_with_incentives(
63+
invitation_code=invitation.code,
64+
invitee_uma="$bob@testvasp2.com",
65+
invitee_phone_number_e164="+520987654321",
66+
invitee_region=lightspark.RegionCode.MX,
67+
)
68+
failed = False
69+
except:
70+
failed = True
71+
72+
assert failed, "Claiming an invitation twice should fail."
73+
print("Claiming an invitation twice failed, as expected!")

lightspark/__init__.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,24 @@
2626
from lightspark.objects.ChannelToTransactionsConnection import (
2727
ChannelToTransactionsConnection,
2828
)
29+
from lightspark.objects.ClaimUmaInvitationInput import ClaimUmaInvitationInput
30+
from lightspark.objects.ClaimUmaInvitationOutput import ClaimUmaInvitationOutput
31+
from lightspark.objects.ClaimUmaInvitationWithIncentivesInput import (
32+
ClaimUmaInvitationWithIncentivesInput,
33+
)
34+
from lightspark.objects.ClaimUmaInvitationWithIncentivesOutput import (
35+
ClaimUmaInvitationWithIncentivesOutput,
36+
)
2937
from lightspark.objects.ComplianceProvider import ComplianceProvider
3038
from lightspark.objects.Connection import Connection
3139
from lightspark.objects.CreateApiTokenInput import CreateApiTokenInput
3240
from lightspark.objects.CreateApiTokenOutput import CreateApiTokenOutput
41+
from lightspark.objects.CreateInvitationWithIncentivesInput import (
42+
CreateInvitationWithIncentivesInput,
43+
)
44+
from lightspark.objects.CreateInvitationWithIncentivesOutput import (
45+
CreateInvitationWithIncentivesOutput,
46+
)
3347
from lightspark.objects.CreateInvoiceInput import CreateInvoiceInput
3448
from lightspark.objects.CreateInvoiceOutput import CreateInvoiceOutput
3549
from lightspark.objects.CreateLnurlInvoiceInput import CreateLnurlInvoiceInput
@@ -41,6 +55,8 @@
4155
from lightspark.objects.CreateTestModeInvoiceOutput import CreateTestModeInvoiceOutput
4256
from lightspark.objects.CreateTestModePaymentInput import CreateTestModePaymentInput
4357
from lightspark.objects.CreateTestModePaymentoutput import CreateTestModePaymentoutput
58+
from lightspark.objects.CreateUmaInvitationInput import CreateUmaInvitationInput
59+
from lightspark.objects.CreateUmaInvitationOutput import CreateUmaInvitationOutput
4460
from lightspark.objects.CreateUmaInvoiceInput import CreateUmaInvoiceInput
4561
from lightspark.objects.CurrencyAmount import CurrencyAmount
4662
from lightspark.objects.CurrencyUnit import CurrencyUnit
@@ -57,6 +73,10 @@
5773
from lightspark.objects.Hop import Hop
5874
from lightspark.objects.HtlcAttemptFailureCode import HtlcAttemptFailureCode
5975
from lightspark.objects.IdAndSignature import IdAndSignature
76+
from lightspark.objects.IncentivesIneligibilityReason import (
77+
IncentivesIneligibilityReason,
78+
)
79+
from lightspark.objects.IncentivesStatus import IncentivesStatus
6080
from lightspark.objects.IncomingPayment import IncomingPayment
6181
from lightspark.objects.IncomingPaymentAttempt import IncomingPaymentAttempt
6282
from lightspark.objects.IncomingPaymentAttemptStatus import IncomingPaymentAttemptStatus
@@ -115,6 +135,7 @@
115135
from lightspark.objects.PayUmaInvoiceInput import PayUmaInvoiceInput
116136
from lightspark.objects.Permission import Permission
117137
from lightspark.objects.PostTransactionData import PostTransactionData
138+
from lightspark.objects.RegionCode import RegionCode
118139
from lightspark.objects.RegisterPaymentInput import RegisterPaymentInput
119140
from lightspark.objects.RegisterPaymentOutput import RegisterPaymentOutput
120141
from lightspark.objects.ReleaseChannelPerCommitmentSecretInput import (
@@ -152,6 +173,7 @@
152173
from lightspark.objects.TransactionFailures import TransactionFailures
153174
from lightspark.objects.TransactionStatus import TransactionStatus
154175
from lightspark.objects.TransactionType import TransactionType
176+
from lightspark.objects.UmaInvitation import UmaInvitation
155177
from lightspark.objects.UpdateChannelPerCommitmentPointInput import (
156178
UpdateChannelPerCommitmentPointInput,
157179
)

lightspark/lightspark_client.py

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright ©, 2022-present, Lightspark Group, Inc. - All Rights Reserved
22

33
import logging
4+
import re
45
from dataclasses import dataclass
56
from datetime import datetime, timedelta, timezone
67
from hashlib import sha256
@@ -45,15 +46,22 @@
4546
from lightspark.objects.PaymentDirection import PaymentDirection
4647
from lightspark.objects.PaymentRequestData import PaymentRequestData
4748
from lightspark.objects.Permission import Permission
49+
from lightspark.objects.RegionCode import RegionCode
4850
from lightspark.objects.RiskRating import RiskRating
4951
from lightspark.objects.TransactionStatus import TransactionStatus
52+
from lightspark.objects.UmaInvitation import UmaInvitation
53+
from lightspark.objects.UmaInvitation import from_json as UmaInvitation_from_json
5054
from lightspark.objects.WithdrawalMode import WithdrawalMode
5155
from lightspark.objects.WithdrawalRequest import WithdrawalRequest
5256
from lightspark.objects.WithdrawalRequest import (
5357
from_json as WithdrawalRequest_from_json,
5458
)
5559
from lightspark.requests.requester import Requester
5660
from lightspark.scripts.bitcoin_fee_estimate import BITCOIN_FEE_ESTIMATE_QUERY
61+
from lightspark.scripts.claim_uma_invitation import (
62+
CLAIM_UMA_INVITATION_MUTATION,
63+
CLAIM_UMA_INVITATION_WITH_INCENTIVES_MUTATION,
64+
)
5765
from lightspark.scripts.create_api_token import CREATE_API_TOKEN_MUTATION
5866
from lightspark.scripts.create_invoice import CREATE_INVOICE_MUTATION
5967
from lightspark.scripts.create_lnurl_invoice import CREATE_LNURL_INVOICE_MUTATION
@@ -64,10 +72,15 @@
6472
from lightspark.scripts.create_test_mode_payment import (
6573
CREATE_TEST_MODE_PAYMENT_MUTATION,
6674
)
75+
from lightspark.scripts.create_uma_invitation import (
76+
CREATE_UMA_INVITATION_MUTATION,
77+
CREATE_UMA_INVITATION_WITH_INCENTIVES_MUTATION,
78+
)
6779
from lightspark.scripts.create_uma_invoice import CREATE_UMA_INVOICE_MUTATION
6880
from lightspark.scripts.current_account import CURRENT_ACCOUNT_QUERY
6981
from lightspark.scripts.decoded_payment_request import DECODED_PAYMENT_REQUEST_QUERY
7082
from lightspark.scripts.delete_api_token import DELETE_API_TOKEN_MUTATION
83+
from lightspark.scripts.fetch_uma_invitation import FETCH_UMA_INVITATION_QUERY
7184
from lightspark.scripts.fund_node import FUND_NODE_MUTATION
7285
from lightspark.scripts.lightning_fee_estimate_for_invoice import (
7386
LIGHTNING_FEE_ESTIMATE_FOR_INVOICE_QUERY,
@@ -87,7 +100,7 @@
87100
from lightspark.scripts.send_payment import SEND_PAYMENT_MUTATION
88101
from lightspark.utils.crypto import decrypt_private_key
89102
from lightspark.utils.enums import parse_enum
90-
from lightspark.utils.signing_key import SigningKey, Secp256k1SigningKey, RSASigningKey
103+
from lightspark.utils.signing_key import RSASigningKey, Secp256k1SigningKey, SigningKey
91104

92105
logger = logging.getLogger("lightspark")
93106

@@ -653,3 +666,138 @@ def outgoing_payments_for_invoice(
653666
OutgoingPayment_from_json(self._requester, payment)
654667
for payment in json["outgoing_payments_for_invoice"]["payments"]
655668
]
669+
670+
def create_uma_invitation(
671+
self,
672+
inviter_uma: str,
673+
) -> UmaInvitation:
674+
"""
675+
Creates a new UMA invitation. If you are part of the incentive program, you should use the
676+
`create_uma_invitation_with_incentives` method instead.
677+
678+
Args:
679+
inviter_uma: The UMA of the inviter.
680+
"""
681+
json = self._requester.execute_graphql(
682+
CREATE_UMA_INVITATION_MUTATION,
683+
{
684+
"inviter_uma": inviter_uma,
685+
},
686+
)
687+
return UmaInvitation_from_json(
688+
self._requester, json["create_uma_invitation"]["invitation"]
689+
)
690+
691+
def create_uma_invitation_with_incentives(
692+
self,
693+
inviter_uma: str,
694+
inviter_phone_number_e164: str,
695+
inviter_region: RegionCode,
696+
) -> UmaInvitation:
697+
"""
698+
Creates a new UMA invitation with incentives. If you are not part of the incentive program, you should use the
699+
`create_uma_invitation` method instead.
700+
701+
Args:
702+
inviter_uma: The UMA of the inviter.
703+
inviter_phone_number_e164: The E.164 formatted phone number of the inviter.
704+
inviter_region: The region of the inviter.
705+
"""
706+
json = self._requester.execute_graphql(
707+
CREATE_UMA_INVITATION_WITH_INCENTIVES_MUTATION,
708+
{
709+
"inviter_uma": inviter_uma,
710+
"inviter_phone_hash": self._hash_phone_number(
711+
inviter_phone_number_e164
712+
),
713+
"inviter_region": inviter_region.name,
714+
},
715+
)
716+
return UmaInvitation_from_json(
717+
self._requester, json["create_uma_invitation_with_incentives"]["invitation"]
718+
)
719+
720+
def claim_uma_invitation(
721+
self,
722+
invitation_code: str,
723+
invitee_uma: str,
724+
) -> None:
725+
"""
726+
Claims a UMA invitation. If you are part of the incentive program, you should use the
727+
`claim_uma_invitation_with_incentives` method instead.
728+
729+
Args:
730+
invitation_code: The invitation code of the invitation to claim.
731+
invitee_uma: The new UMA of the invitee.
732+
"""
733+
self._requester.execute_graphql(
734+
CLAIM_UMA_INVITATION_MUTATION,
735+
{
736+
"invitation_code": invitation_code,
737+
"invitee_uma": invitee_uma,
738+
},
739+
)
740+
741+
def claim_uma_invitation_with_incentives(
742+
self,
743+
invitation_code: str,
744+
invitee_uma: str,
745+
invitee_phone_number_e164: str,
746+
invitee_region: RegionCode,
747+
) -> None:
748+
"""
749+
Claims a UMA invitation with incentives. If you are not part of the incentive program, you should use the
750+
`claim_uma_invitation` method instead.
751+
752+
Args:
753+
invitation_code: The invitation code of the invitation to claim.
754+
invitee_uma: The new UMA of the invitee.
755+
invitee_phone_number_e164: The E.164 formatted phone number of the invitee.
756+
invitee_region: The region of the invitee.
757+
"""
758+
self._requester.execute_graphql(
759+
CLAIM_UMA_INVITATION_WITH_INCENTIVES_MUTATION,
760+
{
761+
"invitation_code": invitation_code,
762+
"invitee_uma": invitee_uma,
763+
"invitee_phone_hash": self._hash_phone_number(
764+
invitee_phone_number_e164
765+
),
766+
"invitee_region": invitee_region,
767+
},
768+
)
769+
770+
def fetch_uma_invitation(
771+
self,
772+
invitation_code: str,
773+
) -> Optional[UmaInvitation]:
774+
"""
775+
Fetches a UMA invitation by its invitation code.
776+
777+
Args:
778+
invitation_code: The invitation code of the invitation to fetch.
779+
"""
780+
json = self._requester.execute_graphql(
781+
FETCH_UMA_INVITATION_QUERY,
782+
{
783+
"invitation_code": invitation_code,
784+
},
785+
)
786+
787+
return (
788+
UmaInvitation_from_json(self._requester, json["uma_invitation_by_code"])
789+
if json["uma_invitation_by_code"]
790+
else None
791+
)
792+
793+
def _hash_phone_number(self, phone_number_e164_format: str) -> str:
794+
match = E614_REGEX.search(phone_number_e164_format)
795+
if not match:
796+
raise LightsparkException(
797+
"InvalidPhoneNumber", "The phone number must follow the E.164 format."
798+
)
799+
return sha256(phone_number_e164_format.encode()).hexdigest()
800+
801+
802+
# pylint: disable=anomalous-backslash-in-string
803+
E614_REGEX = re.compile("^\+?[1-9]\d{1,14}$")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright ©, 2022-present, Lightspark Group, Inc. - All Rights Reserved
2+
3+
from dataclasses import dataclass
4+
from typing import Any, Mapping
5+
6+
7+
@dataclass
8+
class ClaimUmaInvitationInput:
9+
invitation_code: str
10+
11+
invitee_uma: str
12+
13+
def to_json(self) -> Mapping[str, Any]:
14+
return {
15+
"claim_uma_invitation_input_invitation_code": self.invitation_code,
16+
"claim_uma_invitation_input_invitee_uma": self.invitee_uma,
17+
}
18+
19+
20+
def from_json(obj: Mapping[str, Any]) -> ClaimUmaInvitationInput:
21+
return ClaimUmaInvitationInput(
22+
invitation_code=obj["claim_uma_invitation_input_invitation_code"],
23+
invitee_uma=obj["claim_uma_invitation_input_invitee_uma"],
24+
)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Copyright ©, 2022-present, Lightspark Group, Inc. - All Rights Reserved
2+
3+
from dataclasses import dataclass
4+
from typing import Any, Mapping
5+
6+
from lightspark.requests.requester import Requester
7+
8+
9+
@dataclass
10+
class ClaimUmaInvitationOutput:
11+
requester: Requester
12+
13+
invitation_id: str
14+
15+
def to_json(self) -> Mapping[str, Any]:
16+
return {
17+
"claim_uma_invitation_output_invitation": {"id": self.invitation_id},
18+
}
19+
20+
21+
FRAGMENT = """
22+
fragment ClaimUmaInvitationOutputFragment on ClaimUmaInvitationOutput {
23+
__typename
24+
claim_uma_invitation_output_invitation: invitation {
25+
id
26+
}
27+
}
28+
"""
29+
30+
31+
def from_json(requester: Requester, obj: Mapping[str, Any]) -> ClaimUmaInvitationOutput:
32+
return ClaimUmaInvitationOutput(
33+
requester=requester,
34+
invitation_id=obj["claim_uma_invitation_output_invitation"]["id"],
35+
)

0 commit comments

Comments
 (0)