Skip to content

Commit e3fe89d

Browse files
authored
feat: Add Pix QRCode generation (#189)
1 parent 35e1264 commit e3fe89d

File tree

11 files changed

+182
-4
lines changed

11 files changed

+182
-4
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,4 @@ ENV/
108108
.DS_Store
109109

110110
*.p12
111-
*.json
111+
./*.json

examples/README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,7 @@
77

88
## Analisando os dados
99
- [Distribuição de gastos por categoria](./analysis.md#Gastos-por-categoria)
10-
- [Evolução de saldo da NuConta](./analysis.md#Evolução-do-Saldo-da-Nuconta)
10+
- [Evolução de saldo da NuConta](./analysis.md#Evolução-do-Saldo-da-Nuconta)
11+
12+
## Pix
13+
- [Solicitando pagamento](./pix.md)

examples/pix.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Pix
2+
Está implementado o suporte a consulta de chaves do Pix e solicitação de cobrança (QRCode).
3+
4+
## Solicitando um pagamento
5+
```python
6+
from pynubank import Nubank, MockHttpClient
7+
8+
nu = Nubank(MockHttpClient())
9+
10+
data = nu.get_available_pix_keys()
11+
12+
print(data['keys']) # Retorna lista de chaves cadastradas no Pix
13+
14+
print(data['account_id']) # Retorna id da sua conta
15+
16+
# No exemplo abaixo solicitamos uma cobrança de R$ 50,25 utilizando a primeira chave cadastrada
17+
money_request = nu.create_pix_payment_qrcode(data['account_id'], 50.25, data['keys'][0])
18+
19+
# Irá printar o QRCode no terminal
20+
money_request['qr_code'].print_ascii()
21+
22+
# Também é possível gerar uma imagem para ser enviada através de algum sistema
23+
# Nesse caso irá salvar um arquivo qr_code.png que pode ser escaneado pelo app do banco para ser pago
24+
qr = money_request['qr_ocde']
25+
img = qr.make_image()
26+
img.save('qr_code.png')
27+
28+
# Além do QRCode também há uma URL para pagamento
29+
print(money_request['payment_url'])
30+
31+
```

pynubank/nubank.py

+26
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,29 @@ def create_money_request(self, amount: float) -> str:
179179
money_request_response = self._make_graphql_request('create_money_request', payload)
180180

181181
return money_request_response['data']['createMoneyRequest']['moneyRequest']['url']
182+
183+
def get_available_pix_keys(self):
184+
response = self._make_graphql_request('get_pix_keys')
185+
savings_acount = response['data']['viewer']['savingsAccount']
186+
187+
return {'keys': savings_acount['dict']['keys'], 'account_id': savings_acount['id']}
188+
189+
def create_pix_payment_qrcode(self, account_id: str, amount: float, pix_key: dict) -> dict:
190+
payload = {
191+
'createPaymentRequestInput': {
192+
'amount': amount,
193+
'pixAlias': pix_key.get('value'),
194+
"savingsAccountId": account_id
195+
}
196+
}
197+
198+
response = self._make_graphql_request('create_pix_money_request', payload)
199+
200+
data = response['data']['createPaymentRequest']['paymentRequest']
201+
qr = QRCode()
202+
qr.add_data(data['brcode'])
203+
204+
return {
205+
'payment_url': data['url'],
206+
'qr_code': qr
207+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
mutation createPaymentRequest($createPaymentRequestInput: CreatePaymentRequestInput) {
2+
createPaymentRequest(input: $createPaymentRequestInput) {
3+
paymentRequest {
4+
id
5+
amount
6+
message
7+
url
8+
transactionId
9+
pixAlias
10+
brcode
11+
}
12+
}
13+
}

pynubank/queries/get_pix_keys.gql

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
query customerKeys {
2+
viewer {
3+
name
4+
maskedTaxId
5+
savingsAccount {
6+
id
7+
dict {
8+
keys(onlyActive: true) {
9+
id
10+
kind
11+
value
12+
formattedValue
13+
itemDeepLink
14+
badge
15+
}
16+
}
17+
}
18+
}
19+
}

pynubank/utils/mock_http.py

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ def __init__(self):
3333
self._results[('https://mocked-proxy-url/api/proxy/ghostflame_123',
3434
str(prepare_request_body('create_money_request')))] = self._read_data('money')
3535
self._results[('https://mocked-proxy-url/api/proxy/customer_123', '')] = self._read_data('customer')
36+
self._results[('https://mocked-proxy-url/api/proxy/ghostflame_123',
37+
str(prepare_request_body('get_pix_keys')))] = self._read_data('pix_keys')
38+
self._results[('https://mocked-proxy-url/api/proxy/ghostflame_123',
39+
str(prepare_request_body('create_pix_money_request')))] = self._read_data('pix_money_request')
3640

3741
def add_mock_url(self, url: str, graphql_object: str, response_json_name: str):
3842
self._results[(url, graphql_object)] = self._read_data(response_json_name)

pynubank/utils/mocked_responses/customer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"mgm_wait",
2323
"mgm_other_wait"
2424
],
25-
"billing_address_city": "São Paulo",
25+
"billing_address_city": "Sao Paulo",
2626
"phone": "1122334455678",
2727
"billing_address_locality": "Bebedouro",
2828
"name": "John Doe Mary Doe",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"data": {
3+
"viewer": {
4+
"name": "Some Joe Doe",
5+
"maskedTaxId": "•••.456.789-••",
6+
"savingsAccount": {
7+
"id": "xxxxxxxxxxxxxxxxxxxxxxxx",
8+
"dict": {
9+
"maxKeys": 5,
10+
"keysAvailableForRegistration": [
11+
{
12+
"kind": "PHONE"
13+
},
14+
{
15+
"kind": "EMAIL"
16+
},
17+
{
18+
"kind": "EVP"
19+
}
20+
],
21+
"keys": [
22+
{
23+
"id": "zzzzzzzzzzzzzz",
24+
"kind": "CPF",
25+
"value": "12345678912",
26+
"formattedValue": "123.456.789-10",
27+
"itemDeepLink": "nuapp://pix/dict/key-details/zzzzzzzzzzzzzz",
28+
"badge": null
29+
},
30+
{
31+
"id": "yyyyyyyyyyyyyyyyyyyyyyy",
32+
"kind": "EMAIL",
33+
"value": "john@doe",
34+
"formattedValue": "john@doe",
35+
"itemDeepLink": "nuapp://pix/dict/key-details/yyyyyyyyyyyyyyyyyyyyyyy",
36+
"badge": null
37+
}
38+
]
39+
}
40+
}
41+
}
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"data": {
3+
"createPaymentRequest": {
4+
"paymentRequest": {
5+
"id": "zzzzzzzzzzzzzzzzzz",
6+
"amount": 500.0,
7+
"message": null,
8+
"url": "https://nubank.com.br/pagar/tttttt/yyyyyyy",
9+
"transactionId": "iiiiiiii",
10+
"pixAlias": "12345678910",
11+
"brcode": "12464565442165BR.GOV.BCB.PIX42136542416542146542165.005802BR5920John Doe6009SAO PAULOSf5ASF56sf654aA65sa4f6S56fs"
12+
}
13+
}
14+
}
15+
}

tests/test_nubank_client.py

+25-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ def test_get_customer():
314314
assert customer['name'] == 'John Doe Mary Doe'
315315
assert customer['billing_address_number'] == '123'
316316
assert customer['billing_address_line1'] == "Paulista Avenue"
317-
assert customer['billing_address_city'] == "São Paulo"
317+
assert customer['billing_address_city'] == "Sao Paulo"
318318
assert customer['billing_address_locality'] == "Bebedouro"
319319
assert customer['billing_address_state'] == "SP"
320320
assert customer['billing_address_postcode'] == "01234567"
@@ -341,3 +341,27 @@ def test_should_create_money_request():
341341
nubank_client.authenticate_with_qr_code('12345678912', 'hunter12', 'some-uuid')
342342

343343
assert nubank_client.create_money_request(200) == 'https://some.tld/path1/path2'
344+
345+
346+
def test_should_fetch_pix_keys():
347+
nubank_client = Nubank(client=MockHttpClient())
348+
nubank_client.authenticate_with_qr_code('12345678912', 'hunter12', 'some-uuid')
349+
350+
data = nubank_client.get_available_pix_keys()
351+
352+
keys = data['keys']
353+
account_id = data['account_id']
354+
355+
assert len(keys) == 2
356+
assert keys[0]['value'] == '12345678912'
357+
assert account_id == 'xxxxxxxxxxxxxxxxxxxxxxxx'
358+
359+
def test_should_create_pix_money_request():
360+
nubank_client = Nubank(client=MockHttpClient())
361+
nubank_client.authenticate_with_qr_code('12345678912', 'hunter12', 'some-uuid')
362+
363+
keys_data = nubank_client.get_available_pix_keys()
364+
request = nubank_client.create_pix_payment_qrcode('1231231232', 1232213.23, keys_data['keys'][0])
365+
366+
assert request['qr_code'] is not None
367+
assert request['payment_url'] == 'https://nubank.com.br/pagar/tttttt/yyyyyyy'

0 commit comments

Comments
 (0)