├── .gitignore ├── LICENSE ├── README.md ├── cert └── .gitignore ├── cli ├── CertificateGenerator.php ├── cli.php └── functions.php ├── composer.json ├── composer.lock ├── examples ├── certificate │ ├── .gitignore │ ├── README.md │ ├── credentials.example.php │ ├── dashboard.php │ └── index.php └── qrcode │ ├── .gitignore │ ├── README.md │ ├── credentials.example.php │ ├── dashboard.php │ ├── index.php │ └── qrcode.php ├── phpunit.xml ├── src ├── EventTypes │ └── PaymentEventTypes.php ├── Exceptions │ ├── NuException.php │ ├── NuMissingCreditCardException.php │ └── NuRequestException.php ├── Helpers │ └── MyHelpers.php ├── Nubank.php ├── Requests │ ├── Account │ │ ├── Balance.php │ │ ├── Feed.php │ │ ├── InvestmentsDetails.php │ │ ├── InvestmentsYield.php │ │ ├── InvestmentsYields.php │ │ └── MoneyRequest.php │ ├── Authentication │ │ ├── AuthRequest.php │ │ ├── RevokeToken.php │ │ ├── WithCertificate.php │ │ └── WithQrCode.php │ ├── BaseRequest.php │ ├── Boleto │ │ └── Create.php │ ├── Card │ │ ├── BillDetails.php │ │ ├── Bills.php │ │ └── Feed.php │ ├── Discovery.php │ ├── GraphqlQueries │ │ ├── account_balance.gql │ │ ├── account_feed.gql │ │ ├── account_id.gql │ │ ├── account_investments.gql │ │ ├── account_investments_yield.gql │ │ ├── create_boleto.gql │ │ ├── create_money_request.gql │ │ ├── create_pix_money_request.gql │ │ └── get_pix_keys.gql │ ├── GraphqlRequest.php │ └── Pix │ │ ├── AvailableKeys.php │ │ └── QrCode.php ├── Services │ └── MagicAttributes.php └── Utils │ └── PixConstants.php └── tests ├── AccountTest.php ├── AuthenticationWithQrCodeTest.php ├── BaseTestCase.php ├── BillsTest.php ├── BoletoTest.php ├── CardServicesTest.php ├── DiscoveryTest.php ├── PixTest.php └── fixtures ├── account ├── balance.php ├── feed.php ├── investments_details.php ├── investments_yields.php └── money_request.php ├── bills.php ├── boleto ├── barcode.php └── customer.php ├── feed.php ├── lift.php ├── loggedin.php ├── onebill.php ├── pix ├── keys.php └── qr.php └── proxy.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .phpintel 3 | .idea 4 | examples/qrcode/credentials.php -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # phpnubank 2 | 3 | Acesse seus extratos do Nubank pelo PHP (baseado em https://github.com/andreroggeri/pynubank) 4 | 5 | -------------------------------------------------------------------------------- /cert/.gitignore: -------------------------------------------------------------------------------- 1 | *.p12 2 | device_id.txt -------------------------------------------------------------------------------- /cli/CertificateGenerator.php: -------------------------------------------------------------------------------- 1 | login = $login; 26 | $this->password = $password; 27 | $this->deviceId = $deviceId; 28 | $this->encryptedCode = $encryptedCode; 29 | 30 | $this->key1 = $this->generateKey(); 31 | $this->key2 = $this->generateKey(); 32 | 33 | $this->client = new \GuzzleHttp\Client(); 34 | // $this->client = new \GuzzleHttp\Client(['debug'=>true]); 35 | 36 | $discovery = new Discovery($this->client); 37 | 38 | $this->url = $discovery->getAppUrl('gen_certificate'); 39 | } 40 | 41 | public function requestCode() 42 | { 43 | $response = $this->client->post( 44 | $this->url, 45 | [ 46 | 'json' => $this->getPayload(), 47 | 'on_headers' => function (ResponseInterface $response) { 48 | $is401 = ($response->getStatusCode() == 401); 49 | $isAuthenticated = $response->hasHeader('WWW-Authenticate'); 50 | 51 | if ( !$is401 || !$isAuthenticated) { 52 | throw new NuException('Authentication code request failed.'); 53 | } 54 | 55 | $parsed = $this->parseAuthenticateHeaders($response->getHeader('WWW-Authenticate')); 56 | $this->encryptedCode = $parsed['device-authorization_encrypted-code']; 57 | 58 | $this->sentTo = $parsed['sent-to']; 59 | } 60 | ] 61 | ); 62 | } 63 | 64 | public function exchangeCerts($code) 65 | { 66 | if (is_null($this->encryptedCode)) { 67 | throw new NuException("No encrypted code found. Did you call 'request_code' before exchanging certs ?"); 68 | } 69 | 70 | $payload = $this->getPayload(); 71 | $payload['code'] = $code; 72 | $payload['encrypted-code'] = $this->encryptedCode; 73 | 74 | $response = $this->client->post($this->url, ['json' => $payload]); 75 | 76 | if ($response->getStatusCode() !== 200) { 77 | throw new NuRequestException($response); 78 | } 79 | 80 | $data = json_decode($response->getBody(), true); 81 | 82 | return (object)[ 83 | 'cert1' => $this->genCert($this->key1, $data['certificate']) 84 | ]; 85 | } 86 | 87 | private function getPayload() 88 | { 89 | return [ 90 | 'login' => $this->login, 91 | 'password' => $this->password, 92 | 'public_key' => $this->getPublicKey($this->key1), 93 | 'public_key_crypto' => $this->getPublicKey($this->key2), 94 | 'model' => "PhpNubank Client", 95 | 'device_id' => $this->deviceId 96 | ]; 97 | } 98 | 99 | private function genCert($key, $cert) 100 | { 101 | $_key = openssl_pkey_export($key, $keyout, ''); 102 | $path = realpath(__DIR__ . "/..") . "/cert/cert.p12"; 103 | 104 | openssl_pkcs12_export_to_file($cert, $path, $keyout, ''); 105 | } 106 | 107 | private function generateKey() 108 | { 109 | return openssl_pkey_new(array( 110 | 'private_key_bits' => 2048, // Size of Key. 111 | 'private_key_type' => OPENSSL_KEYTYPE_RSA, 112 | )); 113 | } 114 | 115 | private function getPublicKey($key) 116 | { 117 | $publicKeyPem = openssl_pkey_get_details($key)['key']; 118 | $pkey = openssl_pkey_get_public($publicKeyPem); 119 | return openssl_pkey_get_details($pkey)['key']; 120 | } 121 | 122 | private function parseAuthenticateHeaders($headerContent) 123 | { 124 | $chunks = explode(",", current($headerContent)); 125 | 126 | $parsed = []; 127 | foreach($chunks as $chunk) { 128 | $ex = explode("=", $chunk); 129 | $key = str_replace(" ", "_", trim($ex[0])); 130 | $value = str_replace('"', '', $ex[1]); 131 | $parsed[$key] = $value; 132 | } 133 | 134 | return $parsed; 135 | } 136 | 137 | public function getSentTo() 138 | { 139 | return $this->sentTo; 140 | } 141 | 142 | public function getDeviceId() 143 | { 144 | return $this->deviceId; 145 | } 146 | } -------------------------------------------------------------------------------- /cli/cli.php: -------------------------------------------------------------------------------- 1 | requestCode(); 22 | } catch (ClientException $e) { 23 | //dd($e->getMessage()); 24 | $email = $gen->getSentTo(); 25 | log_message( "Email sent to {$email} "); 26 | } catch (NuException $e) { 27 | log_message('Failed to request code. Check your credentials!'); 28 | //log_message($e->getMessage()); 29 | return 0; 30 | } 31 | 32 | log_message( "[>] Type the code received by email: " ); 33 | $code = rtrim(fgets(STDIN)); 34 | 35 | try { 36 | $certs = $gen->exchangeCerts($code); 37 | //save_cert($certs->cert1, 'cert.p12'); 38 | save_device_id($gen->getDeviceId()); 39 | 40 | log_message("Certificates generated successfully. (cert.pem)"); 41 | log_message("Warning, keep these certificates safe (Do not share or version in git)"); 42 | 43 | } catch (NuException $e) { 44 | log_message($e->getMessage()); 45 | return 0; 46 | } 47 | -------------------------------------------------------------------------------- /cli/functions.php: -------------------------------------------------------------------------------- 1 | '', 6 | 'password' => '', 7 | ]; -------------------------------------------------------------------------------- /examples/certificate/dashboard.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PHP Nubank - login com qrcode 11 | 12 | 13 | 14 | 15 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 52 | 53 |
54 |
55 |
56 |

Sua conta

57 | 58 |
59 |
60 |
61 |
Saldo da sua conta
62 |

63 |
64 |
65 |
66 |
67 |
Resultado dos seus investimentos
68 |

69 |
70 |
71 |
72 | 73 |

Últimas 5 movimentações

74 | 75 |
    76 | = 6) continue; 81 | ?> 82 |
  • 83 | 84 | 85 |

    86 |
  • 87 | 88 |
89 |
90 | 91 |
92 |

Receba um pagamento com pix

93 |
94 |

Minhas chaves pix

95 |
    96 | keys as $chave) {?> 97 |
  • 98 | 99 |
    100 | 101 |
    102 |
    103 |
  • 104 | 105 |
106 | 107 |

Escolha o valor

108 |
109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 |
118 | 119 |
120 | 121 |
122 | 123 |
124 | 125 | 126 |
127 |
128 |

Aponte a câmera do seu aplicativo de pagamento

129 | 130 |

payment_code; ?>

131 |
132 |
133 | 134 |
135 |
136 |
137 | 138 |
139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /examples/certificate/index.php: -------------------------------------------------------------------------------- 1 | true]); 10 | $client = new GuzzleHttp\Client(); 11 | 12 | session_start(); 13 | 14 | $nu = new Nubank($client); 15 | 16 | $nu->authenticateWithCert( 17 | $credentials['cpf'], 18 | $credentials['password'], 19 | realpath(__DIR__ . "/../../cert/cert.p12") 20 | ); 21 | 22 | try { 23 | $saldo = $nu->getAccountBalance(); 24 | $investimentos = $nu->getAccountInvestimentsYields(); 25 | $feed = $nu->getAccountFeed(); 26 | $pix = $nu->getAvailablePixKeys(); 27 | 28 | if (isset($_GET['chave_pix']) && isset($_GET['montante'])) { 29 | $chaveAtual = $_GET['chave_pix']; 30 | $chpix = array_filter($pix->keys, function($item) use ($chaveAtual) { 31 | return $item['id'] == $chaveAtual; 32 | }); 33 | 34 | $qrcode = $nu->createAvailablePixPaymentQrCode( 35 | $pix->account_id, 36 | (float)$_GET['montante'], 37 | current($chpix) 38 | ); 39 | } 40 | 41 | require __DIR__ . "/dashboard.php"; 42 | } catch (\Exception $e) { 43 | echo "

{$e->getMessage()}

{$e->getTraceAsString()}

"; 44 | 45 | file_put_contents(__DIR__ . "/../../log/error_log", $e->getTraceAsString()); 46 | } 47 | -------------------------------------------------------------------------------- /examples/qrcode/.gitignore: -------------------------------------------------------------------------------- 1 | credentials.php -------------------------------------------------------------------------------- /examples/qrcode/README.md: -------------------------------------------------------------------------------- 1 | ## -------------------------------------------------------------------------------- /examples/qrcode/credentials.example.php: -------------------------------------------------------------------------------- 1 | '', 6 | 'password' => '', 7 | ]; -------------------------------------------------------------------------------- /examples/qrcode/dashboard.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PHP Nubank - login com qrcode 11 | 12 | 13 | 14 | 15 | 30 | 31 | 32 | 33 | 34 |
35 | 36 | 52 | 53 |
54 |
55 |
56 |

Sua conta

57 | 58 |
59 |
60 |
61 |
Saldo da sua conta
62 |

63 |
64 |
65 |
66 |
67 |
Resultado dos seus investimentos
68 |

69 |
70 |
71 |
72 |
73 |
Saldo da sua conta
74 |

75 |
76 |
77 |
78 | 79 |

Últimas 5 movimentações

80 | 81 |
    82 | = 6) continue; 87 | ?> 88 |
  • 89 | 90 | 91 |

    92 |
  • 93 | 94 |
95 |
96 | 97 |
98 |

Receba um pagamento com pix

99 |
100 |

Minhas chaves pix

101 |
    102 | keys as $chave) {?> 103 |
  • 104 | 105 |
    106 | 107 |
    108 |
    109 |
  • 110 | 111 |
112 | 113 |

Escolha o valor

114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 |
124 | 125 |
126 | 127 |
128 | 129 |
130 | 131 | 132 |
133 |
134 |

Aponte a câmera do seu aplicativo de pagamento

135 | 136 |

payment_code; ?>

137 |
138 |
139 | 140 |
141 |
142 |
143 | 144 |
145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /examples/qrcode/index.php: -------------------------------------------------------------------------------- 1 | true]); 14 | $client = new GuzzleHttp\Client(); 15 | 16 | session_start(); 17 | 18 | $nu = new Nubank($client); 19 | 20 | $nu->authenticateWithQrCode( 21 | $credentials['cpf'], 22 | $credentials['password'], 23 | $_POST['uuid'] 24 | ); 25 | 26 | try { 27 | $saldo = $nu->getAccountBalance(); 28 | $investimentos = $nu->getAccountInvestimentsYields(); 29 | $chaves = $nu->getAvailablePixKeys(); 30 | $feed = $nu->getAccountFeed(); 31 | 32 | if (isset($_GET['chave_pix']) && isset($_GET['montante'])) { 33 | $chaveAtual = $_GET['chave_pix']; 34 | $chpix = array_filter($pix->keys, function($item) use ($chaveAtual) { 35 | return $item['id'] == $chaveAtual; 36 | }); 37 | 38 | $qrcode = $nu->createAvailablePixPaymentQrCode( 39 | $pix->account_id, 40 | (float)$_GET['montante'], 41 | current($chpix) 42 | ); 43 | } 44 | 45 | require __DIR__ . "/dashboard.php"; die(); 46 | } catch (\Exception $e) { 47 | echo "

{$e->getMessage()}

{$e->getTraceAsString()}

"; 48 | 49 | file_put_contents(__DIR__ . "/../../log/error_log", $e->getTraceAsString()); 50 | } 51 | 52 | } 53 | 54 | $qrData = WithQrCode::getQrData(); 55 | 56 | require 'qrcode.php'; 57 | -------------------------------------------------------------------------------- /examples/qrcode/qrcode.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | PHP Nubank - login com qrcode 11 | 12 | 13 | 14 | 15 | 30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 | 38 | 39 |

content; ?>

40 | 41 | 42 |
43 |
44 |
45 | 46 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/EventTypes/PaymentEventTypes.php: -------------------------------------------------------------------------------- 1 | response = $response; 13 | 14 | parent::__construct( 15 | "The request made failed with HTTP status code {$response->getStatusCode()}", 16 | $response->getStatusCode() 17 | ); 18 | } 19 | 20 | public function getResponse() 21 | { 22 | return $this->response; 23 | } 24 | 25 | public function getStatusCode() 26 | { 27 | return $this->response->getStatusCode(); 28 | } 29 | } -------------------------------------------------------------------------------- /src/Helpers/MyHelpers.php: -------------------------------------------------------------------------------- 1 | client = $client; 38 | 39 | $this->discovery = new Discovery($this->client); 40 | } 41 | 42 | public function getClient() 43 | { 44 | return $this->client; 45 | } 46 | 47 | public function setAccessToken($token) 48 | { 49 | $this->token = $token; 50 | //$this->client->addHeader('Authorization', "Bearer {$token}"); 51 | } 52 | 53 | public function authenticateWithQrCode($cpf, $password, $uuid) 54 | { 55 | $req = new WithQrCode($this->client); 56 | $req->setLoginUrls( 57 | $this->discovery->getUrl('login'), 58 | $this->discovery->getAppUrl('lift') 59 | ); 60 | 61 | $authData = $req->passwordAuth($cpf, $password); 62 | 63 | $req->setHeader('Authorization', "Bearer {$authData['access_token']}"); 64 | // dd($authData); 65 | 66 | $response = $req->getLiftData($uuid); 67 | 68 | $this->setAuthRequest($req); 69 | $this->saveAuthData($response); 70 | $this->setAccessToken($authData['access_token']); 71 | } 72 | 73 | private function saveAuthData($authData) 74 | { 75 | $links = $authData['_links']; 76 | 77 | $feed_url_keys = ['events', 'magnitude']; 78 | $bills_url_keys = ['bills_summary']; 79 | $customer_url_keys = ['customer']; 80 | 81 | $this->attribs['feed_url'] = $this->findUrl($feed_url_keys, $links); 82 | $this->attribs['bills_url'] = $this->findUrl($bills_url_keys, $links); 83 | $this->attribs['customer_url'] = $this->findUrl($customer_url_keys, $links); 84 | $this->attribs['query_url'] = $links['ghostflame']['href']; 85 | $this->attribs['revoke_token_url'] = $links['revoke_token']['href']; 86 | } 87 | 88 | private function findUrl($knownKeys, $list) 89 | { 90 | $currentValue = $list; 91 | 92 | foreach ($knownKeys as $value) { 93 | if (!isset($currentValue[$value])) { 94 | $currentValue = null; 95 | break; 96 | } 97 | 98 | $currentValue = $currentValue[$value]; 99 | } 100 | 101 | return $currentValue; 102 | } 103 | 104 | private function setAuthRequest(AuthRequest $request) 105 | { 106 | $this->authRequest = $request; 107 | } 108 | 109 | public function getAuthRequest() 110 | { 111 | return $this->authRequest; 112 | } 113 | 114 | public function getCardFeed() 115 | { 116 | $req = new CardFeed($this->client); 117 | 118 | $req->setConfig($this->authRequest->getConfig()); 119 | $req->setHeader('Authorization', "Bearer {$this->token}"); 120 | 121 | $req->get($this->feed_url); 122 | 123 | return $req; 124 | } 125 | 126 | public function getCardStatements() 127 | { 128 | $feed = $this->getCardFeed(); 129 | 130 | return array_filter($feed->events, function($item) { 131 | return $item['category'] == 'transaction'; 132 | }); 133 | } 134 | 135 | public function getBills() 136 | { 137 | /* 138 | if ($this->bills_url == '') { 139 | throw new NuMissingCreditCardException('Missing url'); 140 | } 141 | */ 142 | 143 | $req = new CardBills($this->client); 144 | 145 | $req->setConfig($this->authRequest->getConfig()); 146 | $req->setHeader('Authorization', "Bearer {$this->token}"); 147 | 148 | $req->get($this->bills_url); 149 | 150 | return $req; 151 | } 152 | 153 | public function getBillDetails($url) 154 | { 155 | $req = new CardBillDetails($this->client); 156 | 157 | $req->setConfig($this->authRequest->getConfig()); 158 | $req->setHeader('Authorization', "Bearer {$this->token}"); 159 | 160 | $req->get($url); 161 | 162 | return $req; 163 | } 164 | 165 | public function authenticateWithCert($cpf, $password, $certPath) 166 | { 167 | $req = new WithCertificate($this->client); 168 | $req->setCert($certPath); 169 | 170 | $url = $this->discovery->getAppUrl('token'); 171 | 172 | $payload = [ 173 | 'grant_type' => 'password', 174 | 'client_id' => 'legacy_client_id', 175 | 'client_secret' => 'legacy_client_secret', 176 | 'login' => $cpf, 177 | 'password' => $password 178 | ]; 179 | 180 | $response = $req->post($url, $payload); 181 | 182 | $this->saveAuthData($response); 183 | $this->setAuthRequest($req); 184 | $this->setAccessToken($response['access_token']); 185 | 186 | return $response['refresh_token']; 187 | } 188 | 189 | public function authenticateWithRefreshToken($refreshToken, $certPath) {} 190 | 191 | public function revokeToken() 192 | { 193 | $req = new RevokeToken($this->client); 194 | 195 | $req->setConfig($this->authRequest->getConfig()); 196 | $req->setHeader('Authorization', "Bearer {$this->token}"); 197 | 198 | $req->post($this->revoke_token_url, []); 199 | 200 | $req->removeHeader('Authorization'); 201 | 202 | $this->setAuthRequest($req); 203 | } 204 | 205 | public function getAccountFeed() 206 | { 207 | $req = new AccountFeed($this->client); 208 | 209 | $req->setConfig($this->authRequest->getConfig()); 210 | $req->setHeader('Authorization', "Bearer {$this->token}"); 211 | 212 | $req->query($this->query_url, 'account_feed'); 213 | 214 | return $req->data['viewer']['savingsAccount']['feed']; 215 | } 216 | 217 | public function getAccountStatements() 218 | { 219 | $feed = $this->getAccountFeed(); 220 | 221 | $statements = array_map('pixTransaction', $feed['statements']); 222 | 223 | return array_filter($statements, function($item) { 224 | return in_array($item['__typename'], PaymentEventTypes::all()); 225 | }); 226 | } 227 | 228 | public function getAccountBalance() 229 | { 230 | $req = new AccountBalance($this->client); 231 | 232 | $req->setConfig($this->authRequest->getConfig()); 233 | $req->setHeader('Authorization', "Bearer {$this->token}"); 234 | 235 | $req->query($this->query_url, 'account_balance'); 236 | 237 | return $req->data['viewer']['savingsAccount']['currentSavingsBalance']['netAmount']; 238 | } 239 | 240 | public function getAccountInvestimentsDetails() 241 | { 242 | $req = new AccountInvestmentsDetails($this->client); 243 | 244 | $req->setConfig($this->authRequest->getConfig()); 245 | $req->setHeader('Authorization', "Bearer {$this->token}"); 246 | 247 | $req->query($this->query_url, 'account_balance'); 248 | 249 | return $req->data['viewer']['savingsAccount']['redeemableDeposits']; 250 | } 251 | 252 | public function getAccountInvestimentsYields($date=null) 253 | { 254 | if (is_null($date)) { 255 | $date = \Carbon\Carbon::now(); 256 | } 257 | 258 | $payload = [ 259 | 'asOf' => $date->endOfMonth()->format("Y-m-d") 260 | ]; 261 | 262 | $req = new AccountInvestmentsYields($this->client); 263 | 264 | $req->setConfig($this->authRequest->getConfig()); 265 | $req->setHeader('Authorization', "Bearer {$this->token}"); 266 | 267 | $req->query($this->query_url, 'account_investments_yield', $payload); 268 | 269 | return $req->label; 270 | } 271 | 272 | public function createBoleto($amount) 273 | { 274 | $req = new CreateBoleto($this->client); 275 | 276 | $req->setConfig($this->authRequest->getConfig()); 277 | $req->setHeader('Authorization', "Bearer {$this->token}"); 278 | 279 | $req->getBarCode($this->query_url, $amount); 280 | 281 | return $req->barcode; 282 | } 283 | 284 | public function createMoneyRequest($amount) 285 | { 286 | $req = new AccountFeed($this->client); 287 | 288 | $req->setConfig($this->authRequest->getConfig()); 289 | $req->setHeader('Authorization', "Bearer {$this->token}"); 290 | 291 | $req->query($this->query_url, 'account_feed'); 292 | 293 | $account_id = $req->data['viewer']['savingsAccount']['id']; 294 | 295 | $payload = [ 296 | 'input' => [ 297 | 'amount' => $amount, 298 | 'savingsAccountId' => $account_id, 299 | ] 300 | ]; 301 | 302 | $request = new AccountMoneyRequest($this->client); 303 | $request->query($this->query_url, 'create_money_request', $payload); 304 | 305 | return $request->data['createMoneyRequest']['moneyRequest']['url']; 306 | } 307 | 308 | public function getAvailablePixKeys() 309 | { 310 | $req = new PixAvailableKeys($this->client); 311 | 312 | $req->setConfig($this->authRequest->getConfig()); 313 | $req->setHeader('Authorization', "Bearer {$this->token}"); 314 | 315 | $req->query($this->query_url, 'get_pix_keys'); 316 | 317 | return $req; 318 | } 319 | 320 | public function createAvailablePixPaymentQrCode($accountId, $amount, $pixKey) 321 | { 322 | $req = new PixQrCode($this->client); 323 | 324 | $req->setConfig($this->authRequest->getConfig()); 325 | $req->setHeader('Authorization', "Bearer {$this->token}"); 326 | 327 | $req->getQrData($this->query_url, $accountId, $amount, $pixKey); 328 | 329 | return $req; 330 | } 331 | } -------------------------------------------------------------------------------- /src/Requests/Account/Balance.php: -------------------------------------------------------------------------------- 1 | attribs = parent::query($url, $queryMap); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Requests/Account/Feed.php: -------------------------------------------------------------------------------- 1 | attribs = parent::query($url, $queryMap); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Requests/Account/InvestmentsDetails.php: -------------------------------------------------------------------------------- 1 | attribs = parent::query($url, $queryMap); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Requests/Account/InvestmentsYield.php: -------------------------------------------------------------------------------- 1 | attribs['label'] = $response['data']['viewer']['productFeatures']['savings']['screens']['detailedBalance']['monthBalanceSection']['yieldSection']['semantics']['label']; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Requests/Account/InvestmentsYields.php: -------------------------------------------------------------------------------- 1 | attribs['label'] = $response['data']['viewer']['productFeatures']['savings']['screens']['detailedBalance']['monthBalanceSection']['yieldSection']['semantics']['label']; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Requests/Account/MoneyRequest.php: -------------------------------------------------------------------------------- 1 | attribs = parent::query($url, $queryMap, $variables); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/Requests/Authentication/AuthRequest.php: -------------------------------------------------------------------------------- 1 | loginUrl = $login; 16 | $this->liftUrl = $lift; 17 | 18 | return $this; 19 | } 20 | } -------------------------------------------------------------------------------- /src/Requests/Authentication/RevokeToken.php: -------------------------------------------------------------------------------- 1 | "password", 14 | "login" => $cpf, 15 | "password" => $password, 16 | "client_id" => "legacy_client_id", 17 | "client_secret" => "legacy_client_secret" 18 | ]; 19 | 20 | //error_log("mandando para login"); 21 | $response = $this->post($this->loginUrl, $payload); 22 | 23 | return $response; 24 | } 25 | 26 | public function getLiftData($uuid) 27 | { 28 | $payload = [ 29 | "qr_code_id" => $uuid, 30 | "type" => "login-webapp", 31 | ]; 32 | 33 | //error_log("mandando para lift data"); 34 | return $this->post($this->liftUrl, $payload); 35 | } 36 | 37 | static public function getQrData() 38 | { 39 | $uuid = Uuid::uuid4(); 40 | 41 | $data = [ 42 | 'content' => $uuid, 43 | 'qr' => (new QRCode)->render($uuid), 44 | ]; 45 | 46 | return (object)$data; 47 | } 48 | } -------------------------------------------------------------------------------- /src/Requests/BaseRequest.php: -------------------------------------------------------------------------------- 1 | client = $guzzle; 19 | $this->headers = [ 20 | 'Content-Type' => 'application/json', 21 | 'X-Correlation-Id' => 'WEB-APP.jO4x1', 22 | 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36', 23 | 'Origin' => 'https://conta.nubank.com.br', 24 | 'Referer' => 'https://conta.nubank.com.br/', 25 | ]; 26 | } 27 | 28 | public function getCert() 29 | { 30 | return $this->cert; 31 | } 32 | 33 | public function setCert($certPath) 34 | { 35 | $this->cert = $certPath; 36 | 37 | return $this; 38 | } 39 | 40 | public function setHeader($name, $value) 41 | { 42 | $this->headers[ $name ] = $value; 43 | 44 | return $this; 45 | } 46 | 47 | public function getHeader($name) 48 | { 49 | if (!isset($this->headers[ $name ])) { 50 | throw new NuException("Header {$name} not found"); 51 | } 52 | 53 | return $this->headers[ $name ]; 54 | } 55 | 56 | public function removeHeader($name) 57 | { 58 | if (isset($this->headers[ $name ])) { 59 | unset($this->headers[ $name ]); 60 | } 61 | } 62 | 63 | public function handleResponse(Response $response) 64 | { 65 | if ($response->getStatusCode() !== 200) { 66 | throw new NuRequestException($response); 67 | } 68 | 69 | return json_decode($response->getBody(), true); 70 | } 71 | 72 | private function rawget($url) 73 | { 74 | $data = [ 'headers' => $this->headers, ]; 75 | 76 | if (!empty($this->cert)) { 77 | $data['cert'] = [$this->cert, '']; 78 | } 79 | 80 | return $this->client->request( 81 | 'GET', 82 | $url, 83 | $data 84 | ); 85 | } 86 | 87 | public function get($url) 88 | { 89 | return $this->handleResponse($this->rawget($url)); 90 | } 91 | 92 | public function post($url, $body) 93 | { 94 | $data = [ 'headers' => $this->headers, 'json' => $body ]; 95 | 96 | if (!empty($this->cert)) { 97 | $data['cert'] = [$this->cert, '']; 98 | } 99 | 100 | $response = $this->client->request( 101 | 'POST', 102 | $url, 103 | $data 104 | ); 105 | return $this->handleResponse($response); 106 | } 107 | 108 | public function getConfig() 109 | { 110 | $data = [ 'headers' => $this->headers ]; 111 | 112 | if (!empty($this->cert)) { 113 | $data['cert'] = $this->cert; 114 | } 115 | 116 | return $data; 117 | } 118 | 119 | public function setConfig($config) 120 | { 121 | if (isset($config['headers'])) { 122 | $this->headers = $config['headers']; 123 | } 124 | 125 | if (isset($config['cert'])) { 126 | $this->cert = $config['cert']; 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/Requests/Boleto/Create.php: -------------------------------------------------------------------------------- 1 | attribs['customer_id'] = $response['data']['viewer']['id']; 16 | 17 | $payload = [ 18 | "input" => [ 19 | "amount" => $amount, 20 | "customerId" => $this->attribs['customer_id'], 21 | ] 22 | ]; 23 | 24 | $boleto_res = $this->query($url, 'create_boleto', $payload); 25 | 26 | $this->attribs['barcode'] = $boleto_res['data']['createTransferInBoleto']['boleto']['readableBarcode']; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Requests/Card/BillDetails.php: -------------------------------------------------------------------------------- 1 | attribs = array_merge($this->attribs, $response); 17 | } 18 | } -------------------------------------------------------------------------------- /src/Requests/Card/Bills.php: -------------------------------------------------------------------------------- 1 | attribs['bills'] = $response['bills']; 25 | } 26 | } -------------------------------------------------------------------------------- /src/Requests/Card/Feed.php: -------------------------------------------------------------------------------- 1 | attribs['as_of'] = $response['feed']['as_of']; 17 | $this->attribs['customer_id'] = $response['feed']['customer_id']; 18 | $this->attribs['_links'] = $response['feed']['_links']; 19 | 20 | $this->attribs['events'] = $response['events']; 21 | } 22 | } -------------------------------------------------------------------------------- /src/Requests/Discovery.php: -------------------------------------------------------------------------------- 1 | updateProxyUrls(); 23 | } 24 | 25 | public function getUrl($name) 26 | { 27 | return $this->_getUrl($name, 'main'); 28 | } 29 | 30 | public function getAppUrl($name) 31 | { 32 | return $this->_getUrl($name, 'app'); 33 | } 34 | 35 | private function updateProxyUrls() 36 | { 37 | // $response = $this->get(self::DISCOVER_URL); 38 | $this->proxyListUrl = $this->get(self::DISCOVER_URL); 39 | 40 | // $response = $this->get(self::DISCOVER_APP_URL); 41 | $this->proxyListAppUrl = $this->get(self::DISCOVER_APP_URL); 42 | } 43 | 44 | private function _getUrl($name, $listName) 45 | { 46 | $list = $this->proxyListUrl; 47 | if ($listName == 'app') { 48 | $list = $this->proxyListAppUrl; 49 | } 50 | 51 | if (!isset($list[$name])) { 52 | throw new NuException("There is no URL discovered for {$name}"); 53 | } 54 | 55 | return $list[$name]; 56 | } 57 | } -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/account_balance.gql: -------------------------------------------------------------------------------- 1 | { 2 | viewer { 3 | savingsAccount { 4 | currentSavingsBalance { 5 | netAmount 6 | } 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/account_feed.gql: -------------------------------------------------------------------------------- 1 | { 2 | viewer { 3 | savingsAccount { 4 | id 5 | feed { 6 | id 7 | __typename 8 | title 9 | detail 10 | postDate 11 | ... on TransferInEvent { 12 | amount 13 | originAccount { 14 | name 15 | } 16 | } 17 | ... on TransferOutEvent { 18 | amount 19 | destinationAccount { 20 | name 21 | } 22 | } 23 | ... on TransferOutReversalEvent { 24 | amount 25 | } 26 | ... on BillPaymentEvent { 27 | amount 28 | } 29 | ... on DebitPurchaseEvent { 30 | amount 31 | } 32 | ... on BarcodePaymentEvent { 33 | amount 34 | } 35 | ... on DebitWithdrawalFeeEvent { 36 | amount 37 | } 38 | ... on DebitWithdrawalEvent { 39 | amount 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/account_id.gql: -------------------------------------------------------------------------------- 1 | query RequestMoney_Query { 2 | viewer { 3 | ...RequestMoney_viewer 4 | id 5 | } 6 | } 7 | 8 | fragment RequestMoney_viewer on Customer { 9 | savingsAccount { 10 | id 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/account_investments.gql: -------------------------------------------------------------------------------- 1 | { 2 | viewer { 3 | savingsAccount { 4 | redeemableDeposits { 5 | id 6 | rate 7 | vehicle 8 | openDate 9 | maturityDate 10 | principal 11 | redeemedBalance{ 12 | netAmount 13 | yield 14 | incomeTax 15 | iofTax 16 | id 17 | } 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/account_investments_yield.gql: -------------------------------------------------------------------------------- 1 | query detailedBalanceScreen($asOf: Date) { 2 | viewer { 3 | productFeatures { 4 | savings { 5 | screens { 6 | detailedBalance(asOf: $asOf) { 7 | monthBalanceSection { 8 | yieldSection { 9 | semantics { 10 | label 11 | } 12 | } 13 | } 14 | } 15 | } 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/create_boleto.gql: -------------------------------------------------------------------------------- 1 | mutation withCreateBoleto_Mutation( 2 | $input: CreateTransferInBoletoInput! 3 | ) { 4 | createTransferInBoleto(input: $input) { 5 | boleto { 6 | id 7 | dueDate 8 | barcode 9 | readableBarcode 10 | amount 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/create_money_request.gql: -------------------------------------------------------------------------------- 1 | mutation createMoneyRequest_createMoneyRequestMutation( 2 | $input: CreateMoneyRequestInput! 3 | ) { 4 | createMoneyRequest(input: $input) { 5 | moneyRequest { 6 | amount 7 | message 8 | url 9 | id 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/create_pix_money_request.gql: -------------------------------------------------------------------------------- 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 | } -------------------------------------------------------------------------------- /src/Requests/GraphqlQueries/get_pix_keys.gql: -------------------------------------------------------------------------------- 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 | } -------------------------------------------------------------------------------- /src/Requests/GraphqlRequest.php: -------------------------------------------------------------------------------- 1 | $data ]; 19 | if (is_array($variables) && count($variables) > 0) { 20 | $payload['variables'] = $variables; 21 | } 22 | 23 | return parent::post($url, $payload); 24 | } 25 | } -------------------------------------------------------------------------------- /src/Requests/Pix/AvailableKeys.php: -------------------------------------------------------------------------------- 1 | attribs = [ 20 | 'keys' => $savingsAccount['dict']['keys'], 21 | 'account_id' => $savingsAccount['id'], 22 | ]; 23 | } 24 | } -------------------------------------------------------------------------------- /src/Requests/Pix/QrCode.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'amount' => $amount, 19 | 'pixAlias' => $pixKey['value'], 20 | "savingsAccountId" => $accountId 21 | ] 22 | ]; 23 | 24 | $response = parent::query($url, 'create_pix_money_request', $payload); 25 | 26 | $data = $response['data']['createPaymentRequest']['paymentRequest']; 27 | 28 | $qr = new QrCodeGenerator(); 29 | 30 | $this->attribs = [ 31 | 'payment_url' => $data['url'], 32 | 'payment_code' => $data['brcode'], 33 | 'qr_code' => $qr->render($data['brcode']), 34 | 'qr' => $qr, 35 | ]; 36 | } 37 | } -------------------------------------------------------------------------------- /src/Services/MagicAttributes.php: -------------------------------------------------------------------------------- 1 | attribs[$key] = $value; 13 | 14 | return $this; 15 | } 16 | 17 | public function __get($key) 18 | { 19 | if (!isset($this->attribs[$key])) { 20 | throw new NuException("Url {$key} is not here"); 21 | } 22 | 23 | return $this->attribs[$key]; 24 | } 25 | 26 | public function attributes() 27 | { 28 | return $this->attribs; 29 | } 30 | } -------------------------------------------------------------------------------- /src/Utils/PixConstants.php: -------------------------------------------------------------------------------- 1 | 'PixTransferInEvent', 15 | TITLE_OUTFLOW_PIX => 'PixTransferOutEvent', 16 | TITLE_REVERSAL_PIX => 'PixTransferOutReversalEvent', 17 | TITLE_FAILED_PIX => 'PixTransferFailedEvent', 18 | ]; 19 | } 20 | } -------------------------------------------------------------------------------- /tests/AccountTest.php: -------------------------------------------------------------------------------- 1 | getFixture('proxy'); 13 | $accessToken = $this->getFixture('loggedin'); 14 | $liftData = $this->getFixture('lift'); 15 | $client = $this->configureMock([ 16 | new Response(200, [], json_encode($proxyData)), 17 | new Response(200, [], json_encode($proxyData)), 18 | new Response(200, [], json_encode($accessToken)), 19 | new Response(200, [], json_encode($liftData)), 20 | ]); 21 | 22 | $nu = new Nubank($client); 23 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 24 | 25 | $this->mock->reset(); 26 | 27 | $feedData = $this->getFixture('account.feed'); 28 | $this->mock->append(new Response(200, [], json_encode($feedData))); 29 | 30 | $stmt = $nu->getAccountFeed()['statements']; 31 | 32 | $this->assertEquals($stmt[0]['id'], 'e409e495-4a16-4bad-9ddb-5c447c84fdcb'); 33 | $this->assertEquals($stmt[0]['__typename'], 'TransferOutEvent'); 34 | $this->assertEquals($stmt[0]['title'], 'Transferência enviada'); 35 | $this->assertEquals($stmt[0]['detail'], 'Waldisney da Silva - R$ 4.496,90'); 36 | $this->assertEquals($stmt[0]['postDate'], '2021-04-14'); 37 | $this->assertEquals($stmt[0]['amount'], '4496.9'); 38 | 39 | $this->assertEquals($stmt[1]['id'], 'acb9a16b-2a1c-40cc-a20b-0778a4503f12'); 40 | $this->assertEquals($stmt[1]['__typename'], 'TransferInEvent'); 41 | $this->assertEquals($stmt[1]['title'], 'Transferência recebida'); 42 | $this->assertEquals($stmt[1]['detail'], 'R$ 1.483,80'); 43 | $this->assertEquals($stmt[1]['postDate'], '2021-04-06'); 44 | $this->assertEquals($stmt[1]['amount'], '1483.8'); 45 | $this->assertEquals($stmt[1]['originAccount']['name'], 'Waldisney da Silva'); 46 | } 47 | 48 | public function testGettingAccountBalance() 49 | { 50 | $proxyData = $this->getFixture('proxy'); 51 | $accessToken = $this->getFixture('loggedin'); 52 | $liftData = $this->getFixture('lift'); 53 | $client = $this->configureMock([ 54 | new Response(200, [], json_encode($proxyData)), 55 | new Response(200, [], json_encode($proxyData)), 56 | new Response(200, [], json_encode($accessToken)), 57 | new Response(200, [], json_encode($liftData)), 58 | ]); 59 | 60 | $nu = new Nubank($client); 61 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 62 | 63 | $this->mock->reset(); 64 | 65 | $balanceData = $this->getFixture('account.balance'); 66 | $this->mock->append(new Response(200, [], json_encode($balanceData))); 67 | 68 | $this->assertEquals($nu->getAccountBalance(), '127.33'); 69 | } 70 | 71 | public function testGettingAccountStatements() 72 | { 73 | $proxyData = $this->getFixture('proxy'); 74 | $accessToken = $this->getFixture('loggedin'); 75 | $liftData = $this->getFixture('lift'); 76 | $client = $this->configureMock([ 77 | new Response(200, [], json_encode($proxyData)), 78 | new Response(200, [], json_encode($proxyData)), 79 | new Response(200, [], json_encode($accessToken)), 80 | new Response(200, [], json_encode($liftData)), 81 | ]); 82 | 83 | $nu = new Nubank($client); 84 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 85 | 86 | $this->mock->reset(); 87 | 88 | $feedData = $this->getFixture('account.feed'); 89 | $this->mock->append(new Response(200, [], json_encode($feedData))); 90 | 91 | $stmt = $nu->getAccountStatements(); 92 | 93 | $this->assertEquals($stmt[0]['id'], 'e409e495-4a16-4bad-9ddb-5c447c84fdcb'); 94 | $this->assertEquals($stmt[0]['__typename'], 'TransferOutEvent'); 95 | $this->assertEquals($stmt[0]['title'], 'Transferência enviada'); 96 | $this->assertEquals($stmt[0]['detail'], 'Waldisney da Silva - R$ 4.496,90'); 97 | $this->assertEquals($stmt[0]['postDate'], '2021-04-14'); 98 | $this->assertEquals($stmt[0]['amount'], '4496.9'); 99 | 100 | $this->assertEquals($stmt[22]['id'], 'a9f96774-37f2-431e-9e6f-a081defacf25'); 101 | $this->assertEquals($stmt[22]['__typename'], 'BarcodePaymentEvent'); 102 | $this->assertEquals($stmt[22]['title'], 'Pagamento efetuado'); 103 | $this->assertEquals($stmt[22]['detail'], 'CONFIDENCE CORRETORA DE CAMBIO S A'); 104 | $this->assertEquals($stmt[22]['postDate'], '2020-12-08'); 105 | $this->assertEquals($stmt[22]['amount'], '4245.10'); 106 | } 107 | 108 | public function testGettingAccountInvestmentsDetails() 109 | { 110 | $proxyData = $this->getFixture('proxy'); 111 | $accessToken = $this->getFixture('loggedin'); 112 | $liftData = $this->getFixture('lift'); 113 | $client = $this->configureMock([ 114 | new Response(200, [], json_encode($proxyData)), 115 | new Response(200, [], json_encode($proxyData)), 116 | new Response(200, [], json_encode($accessToken)), 117 | new Response(200, [], json_encode($liftData)), 118 | ]); 119 | 120 | $nu = new Nubank($client); 121 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 122 | 123 | $this->mock->reset(); 124 | 125 | $feedData = $this->getFixture('account.investments_details'); 126 | $this->mock->append(new Response(200, [], json_encode($feedData))); 127 | 128 | $stmt = $nu->getAccountInvestimentsDetails(); 129 | 130 | $this->assertEquals($stmt[0]['id'], 'vjdhausd-asdg-bgfs-vfsg-jrthfuv'); 131 | $this->assertEquals($stmt[0]['rate'], 1); 132 | $this->assertEquals($stmt[0]['vehicle'], 'RECEIPT_DEPOSIT'); 133 | $this->assertEquals($stmt[0]['openDate'], '2020-07-13'); 134 | $this->assertEquals($stmt[0]['maturityDate'], '2022-07-05'); 135 | $this->assertEquals($stmt[0]['principal'], 156.52); 136 | $this->assertEquals($stmt[0]['redeemedBalance']['netAmount'], 0); 137 | $this->assertEquals($stmt[0]['redeemedBalance']['yield'], 0); 138 | $this->assertEquals($stmt[0]['redeemedBalance']['incomeTax'], 0); 139 | $this->assertEquals($stmt[0]['redeemedBalance']['iofTax'], 0); 140 | $this->assertEquals($stmt[0]['redeemedBalance']['id'], 'abcdefgh-ijkl-mnop-qrst-uvwxyz0123'); 141 | 142 | $this->assertEquals($stmt[2]['id'], 'ffghjyu-ktyu-dfgn-nfgh-asdgre'); 143 | $this->assertEquals($stmt[2]['rate'], 1); 144 | $this->assertEquals($stmt[2]['vehicle'], 'RECEIPT_DEPOSIT'); 145 | $this->assertEquals($stmt[2]['openDate'], '2020-08-11'); 146 | $this->assertEquals($stmt[2]['maturityDate'], '2022-08-03'); 147 | $this->assertEquals($stmt[2]['principal'], 77.77); 148 | $this->assertEquals($stmt[2]['redeemedBalance']['netAmount'], 39.99); 149 | $this->assertEquals($stmt[2]['redeemedBalance']['yield'], 0.05); 150 | $this->assertEquals($stmt[2]['redeemedBalance']['incomeTax'], 0.01); 151 | $this->assertEquals($stmt[2]['redeemedBalance']['iofTax'], 0.01); 152 | $this->assertEquals($stmt[2]['redeemedBalance']['id'], 'sdfgehhdf-jkre-thre-nghh-kuvsnjue633'); 153 | } 154 | 155 | public function testGettingAccountInvestmentsYields() 156 | { 157 | $proxyData = $this->getFixture('proxy'); 158 | $accessToken = $this->getFixture('loggedin'); 159 | $liftData = $this->getFixture('lift'); 160 | $client = $this->configureMock([ 161 | new Response(200, [], json_encode($proxyData)), 162 | new Response(200, [], json_encode($proxyData)), 163 | new Response(200, [], json_encode($accessToken)), 164 | new Response(200, [], json_encode($liftData)), 165 | ]); 166 | 167 | $nu = new Nubank($client); 168 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 169 | 170 | $this->mock->reset(); 171 | 172 | $feedData = $this->getFixture('account.investments_yields'); 173 | $this->mock->append(new Response(200, [], json_encode($feedData)));; 174 | 175 | $this->assertEquals( 176 | $nu->getAccountInvestimentsYields(\Carbon\Carbon::now()), 177 | 0.14 178 | ); 179 | } 180 | 181 | public function testGettingAccountMoneyRequest() 182 | { 183 | $proxyData = $this->getFixture('proxy'); 184 | $accessToken = $this->getFixture('loggedin'); 185 | $liftData = $this->getFixture('lift'); 186 | $client = $this->configureMock([ 187 | new Response(200, [], json_encode($proxyData)), 188 | new Response(200, [], json_encode($proxyData)), 189 | new Response(200, [], json_encode($accessToken)), 190 | new Response(200, [], json_encode($liftData)), 191 | ]); 192 | 193 | $nu = new Nubank($client); 194 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 195 | 196 | $this->mock->reset(); 197 | 198 | $feedData = $this->getFixture('account.feed'); 199 | $mrData = $this->getFixture('account.money_request'); 200 | $this->mock->append(new Response(200, [], json_encode($feedData))); 201 | $this->mock->append(new Response(200, [], json_encode($mrData))); 202 | 203 | $this->assertEquals( 204 | $nu->createMoneyRequest(200), 205 | 'https://some.tld/path1/path2' 206 | ); 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /tests/AuthenticationWithQrCodeTest.php: -------------------------------------------------------------------------------- 1 | configureMock([ 16 | new Response(200, [], json_encode($proxyData)), 17 | new Response(200, [], json_encode($proxyData)), 18 | new Response(200, [], json_encode($accessToken)), 19 | new Response(200, [], json_encode($linksData)), 20 | ]); 21 | 22 | $nu = new Nubank($client); 23 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 24 | 25 | $this->assertEquals($nu->feed_url, 'https://mocked-proxy-url/api/proxy/events_123'); 26 | $this->assertEquals($nu->getAuthRequest()->getHeader('Authorization'), 'Bearer access_token_123'); 27 | } 28 | 29 | public function te_stRevokingCertificate() 30 | { 31 | $this->expectException(NuException::class); 32 | $this->expectExceptionMessage('Header Authorization not found'); 33 | 34 | $proxyData = require __DIR__ . '/fixtures/proxy.php'; 35 | $accessToken = ['access_token' => 'access_token_123']; 36 | $linksData = require __DIR__ . '/fixtures/lift.php'; 37 | $client = $this->configureMock([ 38 | new Response(200, [], json_encode($proxyData)), 39 | new Response(200, [], json_encode($proxyData)), 40 | new Response(200, [], json_encode($accessToken)), 41 | new Response(200, [], json_encode($linksData)), 42 | new Response(200, [], json_encode($linksData)), 43 | ]); 44 | 45 | $nu = new Nubank($client); 46 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 47 | 48 | $this->mock->reset(); 49 | 50 | $this->mock->append(new Response(200, [], '{}')); 51 | 52 | $nu->revokeToken(); 53 | } 54 | } -------------------------------------------------------------------------------- /tests/BaseTestCase.php: -------------------------------------------------------------------------------- 1 | mock = new MockHandler($queue); 20 | $handlerStack = HandlerStack::create($this->mock); 21 | 22 | return new Client(['handler' => $handlerStack, 'debug' => true]); 23 | } 24 | 25 | protected function getFixture($path) 26 | { 27 | $path = str_replace(".", "/", $path); 28 | $data = require __DIR__ . "/fixtures/{$path}.php"; 29 | 30 | return $data; 31 | } 32 | } -------------------------------------------------------------------------------- /tests/BillsTest.php: -------------------------------------------------------------------------------- 1 | getFixture('proxy'); 14 | $accessToken = $this->getFixture('loggedin'); 15 | $liftData = $this->getFixture('lift'); 16 | $client = $this->configureMock([ 17 | new Response(200, [], json_encode($proxyData)), 18 | new Response(200, [], json_encode($proxyData)), 19 | new Response(200, [], json_encode($accessToken)), 20 | new Response(200, [], json_encode($liftData)), 21 | ]); 22 | 23 | $nu = new Nubank($client); 24 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 25 | 26 | $this->mock->reset(); 27 | 28 | $billsData = $this->getFixture('bills'); 29 | $this->mock->append(new Response(200, [], json_encode($billsData))); 30 | 31 | $bills = $nu->getBills(); 32 | 33 | $this->assertEquals( 34 | $bills->bills[2]['_links']['self']['href'], 35 | 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz' 36 | ); 37 | $this->assertEquals( 38 | $bills->bills[2]['href'], 39 | 'nuapp://bill/abcde-fghi-jklmn-opqrst-uvxz' 40 | ); 41 | $this->assertEquals($bills->bills[2]['id'], 'abcde-fghi-jklmn-opqrst-uvxz'); 42 | $this->assertEquals($bills->bills[2]['state'], 'overdue'); 43 | 44 | $this->assertEquals($bills->bills[2]['summary']["adjustments"], "-63.99106066"); 45 | $this->assertEquals($bills->bills[2]['summary']["close_date"], "2018-03-03"); 46 | $this->assertEquals($bills->bills[2]['summary']["due_date"], "2018-03-10"); 47 | $this->assertEquals($bills->bills[2]['summary']["effective_due_date"], "2018-03-12"); 48 | $this->assertEquals($bills->bills[2]['summary']["expenses"], "364.14"); 49 | $this->assertEquals($bills->bills[2]['summary']["fees"], "0"); 50 | $this->assertEquals($bills->bills[2]['summary']["interest"], 0); 51 | $this->assertEquals($bills->bills[2]['summary']["interest_charge"], "0"); 52 | $this->assertEquals($bills->bills[2]['summary']["interest_rate"], "0.1375"); 53 | $this->assertEquals($bills->bills[2]['summary']["interest_reversal"], "0"); 54 | $this->assertEquals($bills->bills[2]['summary']["international_tax"], "0"); 55 | $this->assertEquals($bills->bills[2]['summary']["minimum_payment"], 8003); 56 | $this->assertEquals($bills->bills[2]['summary']["open_date"], "2018-02-03"); 57 | $this->assertEquals($bills->bills[2]['summary']["paid"], 28515); 58 | $this->assertEquals($bills->bills[2]['summary']["past_balance"], -1500); 59 | $this->assertEquals($bills->bills[2]['summary']["payments"], "-960.47"); 60 | $this->assertEquals($bills->bills[2]['summary']["precise_minimum_payment"], "480.02544320601300"); 61 | $this->assertEquals($bills->bills[2]['summary']["precise_total_balance"], "285.152041645013"); 62 | $this->assertEquals($bills->bills[2]['summary']["previous_bill_balance"], "945.473102305013"); 63 | $this->assertEquals($bills->bills[2]['summary']["remaining_minimum_payment"], 0); 64 | $this->assertEquals($bills->bills[2]['summary']["tax"], "0"); 65 | $this->assertEquals($bills->bills[2]['summary']["total_accrued"], "0"); 66 | $this->assertEquals($bills->bills[2]['summary']["total_balance"], 28515); 67 | $this->assertEquals($bills->bills[2]['summary']["total_credits"], "-64.18"); 68 | $this->assertEquals($bills->bills[2]['summary']["total_cumulative"], 30015); 69 | $this->assertEquals($bills->bills[2]['summary']["total_financed"], "0"); 70 | $this->assertEquals($bills->bills[2]['summary']["total_international"], "0"); 71 | $this->assertEquals($bills->bills[2]['summary']["total_national"], "364.32893934"); 72 | $this->assertEquals($bills->bills[2]['summary']["total_payments"], "-960.47"); 73 | } 74 | 75 | public function testBillDetails() 76 | { 77 | $proxyData = $this->getFixture('proxy'); 78 | $accessToken = $this->getFixture('loggedin'); 79 | $liftData = $this->getFixture('lift'); 80 | $client = $this->configureMock([ 81 | new Response(200, [], json_encode($proxyData)), 82 | new Response(200, [], json_encode($proxyData)), 83 | new Response(200, [], json_encode($accessToken)), 84 | new Response(200, [], json_encode($liftData)), 85 | ]); 86 | 87 | $nu = new Nubank($client); 88 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 89 | 90 | $this->mock->reset(); 91 | 92 | $billData = $this->getFixture('onebill'); 93 | $this->mock->append(new Response(200, [], json_encode($billData))); 94 | 95 | $bill = $nu->getBillDetails('https://some-url/bill/123-1234-1234-123'); 96 | 97 | $this->assertEquals( 98 | $bill->_links['barcode']['href'], 99 | 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/boleto/barcode' 100 | ); 101 | $this->assertEquals( 102 | $bill->_links['boleto_email']['href'], 103 | 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/boleto/email' 104 | ); 105 | $this->assertEquals( 106 | $bill->_links['invoice_email']['href'], 107 | 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/invoice/email' 108 | ); 109 | $this->assertEquals( 110 | $bill->_links['self']['href'], 111 | 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst' 112 | ); 113 | $this->assertEquals($bill->account_id, 'abcde-fghi-jklmn-opqrst-uvxz'); 114 | $this->assertEquals($bill->auto_debit_failed, False); 115 | $this->assertEquals($bill->barcode, ''); 116 | $this->assertEquals($bill->id, 'abcde-fghi-jklmn-opqrst-uvxz'); 117 | $this->assertEquals($bill->line_items[0]['amount'], 2390); 118 | $this->assertEquals($bill->line_items[0]['category'], 'Eletronicos'); 119 | $this->assertEquals($bill->line_items[0]['charges'], 1); 120 | $this->assertEquals($bill->line_items[0]['href'], 'nuapp://transaction/abcde-fghi-jklmn-opqrst-uvxz'); 121 | $this->assertEquals($bill->line_items[0]['id'], 'abcde-fghi-jklmn-opqrst-uvxz'); 122 | $this->assertEquals($bill->line_items[0]['index'], 0); 123 | $this->assertEquals($bill->line_items[0]['post_date'], '2015-09-09'); 124 | $this->assertEquals($bill->line_items[0]['title'], 'Mercadopago Mlivre'); 125 | $this->assertEquals($bill->linha_digitavel, ''); 126 | $this->assertEquals($bill->payment_method, 'boleto'); 127 | $this->assertEquals($bill->state, 'overdue'); 128 | $this->assertEquals($bill->status, 'paid'); 129 | $this->assertEquals($bill->summary['adjustments'], '0'); 130 | $this->assertEquals($bill->summary['close_date'], '2015-09-25'); 131 | $this->assertEquals($bill->summary['due_date'], '2015-10-10'); 132 | $this->assertEquals($bill->summary['effective_due_date'], '2015-10-13'); 133 | $this->assertEquals($bill->summary['expenses'], '78.8000'); 134 | $this->assertEquals($bill->summary['fees'], '0'); 135 | $this->assertEquals($bill->summary['interest'], 0); 136 | $this->assertEquals($bill->summary['interest_charge'], '0'); 137 | $this->assertEquals($bill->summary['interest_rate'], '0.0775'); 138 | $this->assertEquals($bill->summary['interest_reversal'], '0'); 139 | $this->assertEquals($bill->summary['international_tax'], '0'); 140 | $this->assertEquals($bill->summary['late_fee'], '0.02'); 141 | $this->assertEquals($bill->summary['late_interest_rate'], '0.0875'); 142 | $this->assertEquals($bill->summary['minimum_payment'], 7005); 143 | $this->assertEquals($bill->summary['open_date'], '2015-07-23'); 144 | $this->assertEquals($bill->summary['paid'], 7880); 145 | $this->assertEquals($bill->summary['past_balance'], 0); 146 | $this->assertEquals($bill->summary['payments'], '0'); 147 | $this->assertEquals($bill->summary['precise_minimum_payment'], '70.054500'); 148 | $this->assertEquals($bill->summary['precise_total_balance'], '78.8000'); 149 | $this->assertEquals($bill->summary['previous_bill_balance'], '0'); 150 | $this->assertEquals($bill->summary['tax'], '0'); 151 | $this->assertEquals($bill->summary['total_accrued'], '0'); 152 | $this->assertEquals($bill->summary['total_balance'], 7880); 153 | $this->assertEquals($bill->summary['total_credits'], '0'); 154 | $this->assertEquals($bill->summary['total_cumulative'], 7880); 155 | $this->assertEquals($bill->summary['total_financed'], '0'); 156 | $this->assertEquals($bill->summary['total_international'], '0'); 157 | $this->assertEquals($bill->summary['total_national'], '78.8000'); 158 | $this->assertEquals($bill->summary['total_payments'], '0'); 159 | } 160 | 161 | public function testBillMissingCreditCard() 162 | { 163 | $this->expectException(NuMissingCreditCardException::class); 164 | 165 | $proxyData = $this->getFixture('proxy'); 166 | $accessToken = $this->getFixture('loggedin'); 167 | $liftData = $this->getFixture('lift'); 168 | 169 | //unset($liftData['_links']['bills_summary']); 170 | //var_dump($liftData['_links']); die(); 171 | 172 | $client = $this->configureMock([ 173 | new Response(200, [], json_encode($proxyData)), 174 | new Response(200, [], json_encode($proxyData)), 175 | new Response(200, [], json_encode($accessToken)), 176 | new Response(200, [], json_encode($liftData)), 177 | ]); 178 | 179 | $nu = new Nubank($client); 180 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 181 | 182 | //var_dump($nu); die(); 183 | 184 | $this->mock->reset(); 185 | 186 | $this->mock->append(new Response(200, [], 'Missing card')); 187 | 188 | $nu->getBills(); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /tests/BoletoTest.php: -------------------------------------------------------------------------------- 1 | getFixture('proxy'); 14 | $accessToken = $this->getFixture('loggedin'); 15 | $liftData = $this->getFixture('lift'); 16 | $client = $this->configureMock([ 17 | new Response(200, [], json_encode($proxyData)), 18 | new Response(200, [], json_encode($proxyData)), 19 | new Response(200, [], json_encode($accessToken)), 20 | new Response(200, [], json_encode($liftData)), 21 | ]); 22 | 23 | $nu = new Nubank($client); 24 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 25 | 26 | $this->mock->reset(); 27 | 28 | $pixKeysData = $this->getFixture('boleto.customer'); 29 | $this->mock->append(new Response(200, [], json_encode($pixKeysData))); 30 | 31 | $pixKeysData = $this->getFixture('boleto.barcode'); 32 | $this->mock->append(new Response(200, [], json_encode($pixKeysData))); 33 | 34 | $this->assertEquals($nu->createBoleto(200.50), '123131321231231.2313212312.2131231.21332123'); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/CardServicesTest.php: -------------------------------------------------------------------------------- 1 | getFixture('proxy'); 12 | $accessToken = $this->getFixture('loggedin'); 13 | $liftData = $this->getFixture('lift'); 14 | $client = $this->configureMock([ 15 | new Response(200, [], json_encode($proxyData)), 16 | new Response(200, [], json_encode($proxyData)), 17 | new Response(200, [], json_encode($accessToken)), 18 | new Response(200, [], json_encode($liftData)), 19 | ]); 20 | 21 | $nu = new Nubank($client); 22 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 23 | 24 | $this->mock->reset(); 25 | 26 | $feedData = $this->getFixture('feed'); 27 | $this->mock->append(new Response(200, [], json_encode($feedData))); 28 | 29 | $feed = $nu->getCardFeed(); 30 | 31 | $this->assertEquals($feed->as_of, '2017-09-09T06:50:22.323Z'); 32 | $this->assertEquals($feed->customer_id, 'abcde-fghi-jklmn-opqrst-uvxz'); 33 | $this->assertEquals( 34 | $feed->_links['updates']['href'], 35 | 'https://prod-s0-webapp-proxy.nubank.com.br/api/proxy/updates_123' 36 | ); 37 | $this->assertEquals( 38 | $feed->_links['next']['href'], 39 | 'https://prod-s0-webapp-proxy.nubank.com.br/api/proxy/next_123' 40 | ); 41 | 42 | $this->assertEquals($feed->events[0]['description'], 'Netflix.Com'); 43 | $this->assertEquals($feed->events[0]['category'], 'transaction'); 44 | $this->assertEquals($feed->events[0]['amount'], '3290'); 45 | $this->assertEquals($feed->events[0]['time'], '2021-04-21T10:01:48Z'); 46 | $this->assertEquals($feed->events[0]['title'], 'serviços'); 47 | $this->assertEquals($feed->events[0]['id'], '43e713a0-07b7-43bb-9700-8d7ad2d5eee6'); 48 | $this->assertEquals( 49 | $feed->events[0]['details']['subcategory'], 50 | 'card_not_present' 51 | ); 52 | $this->assertEquals( 53 | $feed->events[0]['href'], 54 | 'nuapp://transaction/43e713a0-07b7-43bb-9700-8d7ad2d5eee6' 55 | ); 56 | $this->assertEquals( 57 | $feed->events[0]['_links']['self']['href'], 58 | 'https://prod-s0-facade.nubank.com.br/api/transactions/43e713a0-07b7-43bb-9700-8d7ad2d5eee6' 59 | ); 60 | } 61 | 62 | public function testGetCardFeedStatements() 63 | { 64 | $proxyData = $this->getFixture('proxy'); 65 | $accessToken = $this->getFixture('loggedin'); 66 | $liftData = $this->getFixture('lift'); 67 | $client = $this->configureMock([ 68 | new Response(200, [], json_encode($proxyData)), 69 | new Response(200, [], json_encode($proxyData)), 70 | new Response(200, [], json_encode($accessToken)), 71 | new Response(200, [], json_encode($liftData)), 72 | ]); 73 | 74 | $nu = new Nubank($client); 75 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 76 | 77 | $this->mock->reset(); 78 | 79 | $stmtData = $this->getFixture('feed'); 80 | $this->mock->append(new Response(200, [], json_encode($stmtData))); 81 | 82 | $stmt = $nu->getCardStatements(); 83 | 84 | $this->assertEquals($stmt[0]['description'], 'Netflix.Com'); 85 | $this->assertEquals($stmt[0]['category'], 'transaction'); 86 | $this->assertEquals($stmt[0]['amount'], '3290'); 87 | $this->assertEquals($stmt[0]['time'], '2021-04-21T10:01:48Z'); 88 | $this->assertEquals($stmt[0]['title'], 'serviços'); 89 | $this->assertEquals($stmt[0]['id'], '43e713a0-07b7-43bb-9700-8d7ad2d5eee6'); 90 | $this->assertEquals($stmt[0]['details']['subcategory'], 'card_not_present'); 91 | $this->assertEquals($stmt[0]['href'], 'nuapp://transaction/43e713a0-07b7-43bb-9700-8d7ad2d5eee6'); 92 | 93 | $this->assertEquals( 94 | $stmt[0]['_links']['self']['href'], 95 | 'https://prod-s0-facade.nubank.com.br/api/transactions/43e713a0-07b7-43bb-9700-8d7ad2d5eee6' 96 | ); 97 | } 98 | } -------------------------------------------------------------------------------- /tests/DiscoveryTest.php: -------------------------------------------------------------------------------- 1 | configureMock([ 16 | new Response(200, [], json_encode($proxyData)), 17 | new Response(200, [], json_encode($proxyData)), 18 | ]); 19 | 20 | $d = new Discovery($client); 21 | 22 | $this->assertEquals($d->getUrl('token'), $proxyData['token']); 23 | } 24 | 25 | public function testUrlNotExists() 26 | { 27 | $this->expectException(NuException::class); 28 | 29 | $proxyData = require __DIR__ . '/fixtures/proxy.php'; 30 | 31 | $client = $this->configureMock([ 32 | new Response(200, [], json_encode($proxyData)), 33 | new Response(200, [], json_encode($proxyData)), 34 | ]); 35 | 36 | $d = new Discovery($client); 37 | 38 | $d->getUrl('some-url'); 39 | } 40 | } -------------------------------------------------------------------------------- /tests/PixTest.php: -------------------------------------------------------------------------------- 1 | getFixture('proxy'); 13 | $accessToken = $this->getFixture('loggedin'); 14 | $liftData = $this->getFixture('lift'); 15 | $client = $this->configureMock([ 16 | new Response(200, [], json_encode($proxyData)), 17 | new Response(200, [], json_encode($proxyData)), 18 | new Response(200, [], json_encode($accessToken)), 19 | new Response(200, [], json_encode($liftData)), 20 | ]); 21 | 22 | $nu = new Nubank($client); 23 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 24 | 25 | $this->mock->reset(); 26 | 27 | $pixKeysData = $this->getFixture('pix.keys'); 28 | $this->mock->append(new Response(200, [], json_encode($pixKeysData))); 29 | 30 | $keys = $nu->getAvailablePixKeys(); 31 | //var_dump($keys); die(); 32 | 33 | $this->assertEquals($keys->account_id, 'xxxxxxxxxxxxxxxxxxxxxxxx'); 34 | $this->assertEquals($keys->keys[0]['value'], '12345678912'); 35 | } 36 | 37 | public function testCreatingAvailablePixPaymentQrCode() 38 | { 39 | $proxyData = $this->getFixture('proxy'); 40 | $accessToken = $this->getFixture('loggedin'); 41 | $liftData = $this->getFixture('lift'); 42 | $client = $this->configureMock([ 43 | new Response(200, [], json_encode($proxyData)), 44 | new Response(200, [], json_encode($proxyData)), 45 | new Response(200, [], json_encode($accessToken)), 46 | new Response(200, [], json_encode($liftData)), 47 | ]); 48 | 49 | $nu = new Nubank($client); 50 | $nu->authenticateWithQrCode('12345678912', 'hunter12', 'some-uuid'); 51 | 52 | $this->mock->reset(); 53 | 54 | $pixKeysData = $this->getFixture('pix.keys'); 55 | $this->mock->append(new Response(200, [], json_encode($pixKeysData))); 56 | 57 | $keys = $nu->getAvailablePixKeys(); 58 | 59 | $this->mock->reset(); 60 | 61 | $pixQrData = $this->getFixture('pix.qr'); 62 | $this->mock->append(new Response(200, [], json_encode($pixQrData))); 63 | 64 | //var_dump($keysData); die(); 65 | $request = $nu->createAvailablePixPaymentQrCode('1231231232', 1232213.23, $keys->keys[0]); 66 | 67 | $this->assertNotNull($request->qr_code); 68 | 69 | $payment_code = [ 70 | '12464565442165BR.GOV.BCB.PIX42136542416542146542165.005802BR5920John ', 71 | 'Doe6009SAO PAULOSf5ASF56sf654aA65sa4f6S56fs' 72 | ]; 73 | $this->assertEquals($request->payment_code, implode("", $payment_code)); 74 | $this->assertEquals($request->payment_url, 'https://nubank.com.br/pagar/tttttt/yyyyyyy'); 75 | } 76 | } -------------------------------------------------------------------------------- /tests/fixtures/account/balance.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'viewer' => [ 5 | 'savingsAccount' => [ 6 | 'currentSavingsBalance' => [ 7 | 'netAmount' => '127.33' 8 | ], 9 | ], 10 | ], 11 | ] 12 | ]; -------------------------------------------------------------------------------- /tests/fixtures/account/feed.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'viewer' => [ 5 | 'savingsAccount' => [ 6 | 'id' => '1234-12345-12345-123', 7 | 'feed' => [ 8 | 'statements' => [ 9 | 0 => [ 10 | 'id' => 'e409e495-4a16-4bad-9ddb-5c447c84fdcb', 11 | '__typename' => 'TransferOutEvent', 12 | 'title' => 'Transferência enviada', 13 | 'detail' => 'Waldisney da Silva - R$ 4.496,90', 14 | 'postDate' => '2021-04-14', 15 | 'amount' => '4496.9', 16 | ], 17 | 1 => [ 18 | 'id' => 'acb9a16b-2a1c-40cc-a20b-0778a4503f12', 19 | '__typename' => 'TransferInEvent', 20 | 'title' => 'Transferência recebida', 21 | 'detail' => 'R$ 1.483,80', 22 | 'postDate' => '2021-04-06', 23 | 'amount' => '1483.8', 24 | 'originAccount' => [ 25 | 'name' => 'Waldisney da Silva' 26 | ], 27 | ], 28 | 22 => [ 29 | 'id' => 'a9f96774-37f2-431e-9e6f-a081defacf25', 30 | '__typename' => 'BarcodePaymentEvent', 31 | 'title' => 'Pagamento efetuado', 32 | 'detail' => 'CONFIDENCE CORRETORA DE CAMBIO S A', 33 | 'postDate' => '2020-12-08', 34 | 'amount' => '4245.10', 35 | ], 36 | ], 37 | ], 38 | ], 39 | ], 40 | ], 41 | ]; -------------------------------------------------------------------------------- /tests/fixtures/account/investments_details.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'viewer' => [ 6 | 'savingsAccount' => [ 7 | 'redeemableDeposits' => [ 8 | 0 => [ 9 | 'id' => 'vjdhausd-asdg-bgfs-vfsg-jrthfuv', 10 | 'rate' => 1, 11 | 'vehicle' => 'RECEIPT_DEPOSIT', 12 | 'openDate' => '2020-07-13', 13 | 'maturityDate' => '2022-07-05', 14 | 'principal' => 156.52, 15 | 'redeemedBalance' => [ 16 | 'netAmount' => 0, 17 | 'yield' => 0, 18 | 'incomeTax' => 0, 19 | 'iofTax' => 0, 20 | 'id' => 'abcdefgh-ijkl-mnop-qrst-uvwxyz0123', 21 | ] 22 | ], 23 | 2 => [ 24 | 'id' => 'ffghjyu-ktyu-dfgn-nfgh-asdgre', 25 | 'rate' => 1, 26 | 'vehicle' => 'RECEIPT_DEPOSIT', 27 | 'openDate' => '2020-08-11', 28 | 'maturityDate' => '2022-08-03', 29 | 'principal' => 77.77, 30 | 'redeemedBalance' => [ 31 | 'netAmount' => 39.99, 32 | 'yield' => 0.05, 33 | 'incomeTax' => 0.01, 34 | 'iofTax' => 0.01, 35 | 'id' => 'sdfgehhdf-jkre-thre-nghh-kuvsnjue633', 36 | ] 37 | ] 38 | ], 39 | ], 40 | ], 41 | ], 42 | ]; -------------------------------------------------------------------------------- /tests/fixtures/account/investments_yields.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'viewer' => [ 5 | 'productFeatures' => [ 6 | 'savings' => [ 7 | 'screens' => [ 8 | 'detailedBalance' => [ 9 | 'monthBalanceSection' => [ 10 | 'yieldSection' => [ 11 | 'semantics' => [ 12 | 'label' => 0.14 13 | ] 14 | ] 15 | ] 16 | ] 17 | ] 18 | ] 19 | ], 20 | ], 21 | ], 22 | ]; -------------------------------------------------------------------------------- /tests/fixtures/account/money_request.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'createMoneyRequest' => [ 5 | 'moneyRequest' => [ 6 | 'url' => 'https://some.tld/path1/path2' 7 | ], 8 | ], 9 | ], 10 | ]; 11 | -------------------------------------------------------------------------------- /tests/fixtures/bills.php: -------------------------------------------------------------------------------- 1 | [ 4 | [], 5 | [], 6 | [ 7 | 'href' => 'nuapp://bill/abcde-fghi-jklmn-opqrst-uvxz', 8 | 'id' => 'abcde-fghi-jklmn-opqrst-uvxz', 9 | 'state' => 'overdue', 10 | '_links' => [ 11 | 'self' => [ 12 | 'href' => "https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz" 13 | ] 14 | ], 15 | 'summary' => [ 16 | "adjustments" => "-63.99106066", 17 | "close_date" => "2018-03-03", 18 | "due_date" => "2018-03-10", 19 | "effective_due_date" => "2018-03-12", 20 | "expenses" => "364.14", 21 | "fees" => "0", 22 | "interest" => 0, 23 | "interest_charge" => "0", 24 | "interest_rate" => "0.1375", 25 | "interest_reversal" => "0", 26 | "international_tax" => "0", 27 | "minimum_payment" => 8003, 28 | "open_date" => "2018-02-03", 29 | "paid" => 28515, 30 | "past_balance" => -1500, 31 | "payments" => "-960.47", 32 | "precise_minimum_payment" => "480.02544320601300", 33 | "precise_total_balance" => "285.152041645013", 34 | "previous_bill_balance" => "945.473102305013", 35 | "remaining_minimum_payment" => 0, 36 | "tax" => "0", 37 | "total_accrued" => "0", 38 | "total_balance" => 28515, 39 | "total_credits" => "-64.18", 40 | "total_cumulative" => 30015, 41 | "total_financed" => "0", 42 | "total_international" => "0", 43 | "total_national" => "364.32893934", 44 | "total_payments" => "-960.47", 45 | ] 46 | ] 47 | ] 48 | ]; -------------------------------------------------------------------------------- /tests/fixtures/boleto/barcode.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'createTransferInBoleto' => [ 5 | 'boleto' => [ 6 | 'id' => '99887711', 7 | 'dueDate' => '', 8 | 'barcode' => '', 9 | 'readableBarcode' => '123131321231231.2313212312.2131231.21332123', 10 | 'amount' => '200.50', 11 | ] 12 | ] 13 | ] 14 | ]; -------------------------------------------------------------------------------- /tests/fixtures/boleto/customer.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'viewer' => [ 5 | 'id' => '1029384756' 6 | ] 7 | ] 8 | ]; -------------------------------------------------------------------------------- /tests/fixtures/feed.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'as_of' => '2017-09-09T06:50:22.323Z', 5 | 'customer_id' => 'abcde-fghi-jklmn-opqrst-uvxz', 6 | '_links' => [ 7 | 'updates' => [ 8 | 'href' => 'https://prod-s0-webapp-proxy.nubank.com.br/api/proxy/updates_123' 9 | ], 10 | 'next' => [ 11 | 'href' => 'https://prod-s0-webapp-proxy.nubank.com.br/api/proxy/next_123' 12 | ] 13 | ], 14 | ], 15 | 'events' => [ 16 | [ 17 | 'id' => '43e713a0-07b7-43bb-9700-8d7ad2d5eee6', 18 | 'description' => 'Netflix.Com', 19 | 'category' => 'transaction', 20 | 'amount' => 3290, 21 | 'time' => '2021-04-21T10:01:48Z', 22 | 'title' => 'serviços', 23 | 'details' => ['subcategory' => 'card_not_present'], 24 | 'href' => 'nuapp://transaction/43e713a0-07b7-43bb-9700-8d7ad2d5eee6', 25 | '_links' => [ 26 | 'self' => [ 27 | 'href' => 'https://prod-s0-facade.nubank.com.br/api/transactions/43e713a0-07b7-43bb-9700-8d7ad2d5eee6' 28 | ] 29 | ] 30 | ] 31 | ] 32 | ]; -------------------------------------------------------------------------------- /tests/fixtures/lift.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'events' => [ 5 | 'magnitude' => 'https://mocked-proxy-url/api/proxy/events_123', 6 | ], 7 | 'bills_summary' => 'https://mocked-proxy-url/api/proxy/bls_123', 8 | 'customer' => [ 9 | 'customer_url' =>'https://mocked-proxy-url/api/proxy/events_123', 10 | ], 11 | 'ghostflame' => [ 12 | 'href' => 'https://mocked-proxy-url/api/proxy/events_123' 13 | ], 14 | 'revoke_token' => [ 15 | 'href' => 'https://mocked-proxy-url/api/proxy/events_123' 16 | ] 17 | ] 18 | ]; -------------------------------------------------------------------------------- /tests/fixtures/loggedin.php: -------------------------------------------------------------------------------- 1 | "access_token_123", 4 | "token_type" =>"bearer", 5 | "_links" => [ 6 | "revoke_token" => [ 7 | "href" => "https://some-url/revoke_token" 8 | ], 9 | "revoke_all" => [ 10 | "href" => "https://some-url/revoke_all" 11 | ], 12 | "account_emergency" => [ 13 | "href" => "https://some-url/account_emergency" 14 | ], 15 | "bill_emergency" => [ 16 | "href" => "https://some-url/bill_emergency" 17 | ] 18 | ], 19 | "refresh_token" => "string token", 20 | "refresh_before" => "2021-08-11T04:23:53Z" 21 | ]; 22 | -------------------------------------------------------------------------------- /tests/fixtures/onebill.php: -------------------------------------------------------------------------------- 1 | 'abcde-fghi-jklmn-opqrst-uvxz', 4 | 'auto_debit_failed' => False, 5 | 'barcode' => '', 6 | 'linha_digitavel' => '', 7 | 'payment_method' => 'boleto', 8 | 'state' => 'overdue', 9 | 'status' => 'paid', 10 | 'id' => 'abcde-fghi-jklmn-opqrst-uvxz', 11 | '_links' => [ 12 | 'barcode' => [ 13 | 'href' => 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/boleto/barcode' 14 | ], 15 | 'boleto_email' => [ 16 | 'href' => 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/boleto/email' 17 | ], 18 | 'invoice_email' => [ 19 | 'href' => 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst-uvxz/invoice/email' 20 | ], 21 | 'self' => [ 22 | 'href' => 'https://mocked-proxy-url/api/bills/abcde-fghi-jklmn-opqrst', 23 | ], 24 | ], 25 | 'summary' => [ 26 | 'adjustments' => '0', 27 | 'close_date' => '2015-09-25', 28 | 'due_date' => '2015-10-10', 29 | 'effective_due_date' => '2015-10-13', 30 | 'expenses' => '78.8000', 31 | 'fees' => '0', 32 | 'interest' => 0, 33 | 'interest_charge' => '0', 34 | 'interest_rate' => '0.0775', 35 | 'interest_reversal' => '0', 36 | 'international_tax' => '0', 37 | 'late_fee' => '0.02', 38 | 'late_interest_rate' => '0.0875', 39 | 'minimum_payment' => 7005, 40 | 'open_date' => '2015-07-23', 41 | 'paid' => 7880, 42 | 'past_balance' => 0, 43 | 'payments' => '0', 44 | 'precise_minimum_payment' => '70.054500', 45 | 'precise_total_balance' => '78.8000', 46 | 'previous_bill_balance' => '0', 47 | 'tax' => '0', 48 | 'total_accrued' => '0', 49 | 'total_balance' => 7880, 50 | 'total_credits' => '0', 51 | 'total_cumulative' => 7880, 52 | 'total_financed' => '0', 53 | 'total_international' => '0', 54 | 'total_national' => '78.8000', 55 | 'total_payments' => '0', 56 | ], 57 | 'line_items' => [ 58 | [ 59 | 'amount' => 2390, 60 | 'category' => 'Eletronicos', 61 | 'charges' => 1, 62 | 'href' => 'nuapp://transaction/abcde-fghi-jklmn-opqrst-uvxz', 63 | 'id' => 'abcde-fghi-jklmn-opqrst-uvxz', 64 | 'index' => 0, 65 | 'post_date' => '2015-09-09', 66 | 'title' => 'Mercadopago Mlivre', 67 | ] 68 | ] 69 | ]; -------------------------------------------------------------------------------- /tests/fixtures/pix/keys.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'viewer' => [ 5 | 'name' => 'Master Blaster', 6 | 'maskedTaxId' => '1.99', 7 | 'savingsAccount' => [ 8 | 'id' => 'xxxxxxxxxxxxxxxxxxxxxxxx', 9 | 'dict' => [ 10 | 'keys' => [ 11 | [ 12 | 'id' => 'xxxxxxxxxxxxxxxxxxxxxxxx', 13 | 'kind' => '', 14 | 'value' => '12345678912', 15 | 'formattedValue' => '', 16 | 'itemDeepLink' => '', 17 | 'badge' => '', 18 | ] 19 | ] 20 | ] 21 | ] 22 | ] 23 | ] 24 | ]; -------------------------------------------------------------------------------- /tests/fixtures/pix/qr.php: -------------------------------------------------------------------------------- 1 | [ 11 | 'createPaymentRequest' => [ 12 | 'paymentRequest' => [ 13 | 'url' => 'https://nubank.com.br/pagar/tttttt/yyyyyyy', 14 | 'brcode' => $brcode, 15 | ] 16 | ] 17 | ] 18 | ]; -------------------------------------------------------------------------------- /tests/fixtures/proxy.php: -------------------------------------------------------------------------------- 1 | 'https://some-url/token', 4 | 'login' =>'https://some-url/login', 5 | 'lift' =>'https://some-url/api/proxy/B', 6 | 'gen_certificate' =>'https://some-url/gen_cert' 7 | ]; --------------------------------------------------------------------------------