diff --git a/app-template/index-template.html b/app-template/index-template.html index 72735d05b14..bc515522dab 100644 --- a/app-template/index-template.html +++ b/app-template/index-template.html @@ -4,10 +4,12 @@ *USERVISIBLENAME* - + - + @@ -22,7 +24,8 @@
- +
diff --git a/src/components/info-sheet/info-sheet.html b/src/components/info-sheet/info-sheet.html index 6140c385979..0de82932970 100644 --- a/src/components/info-sheet/info-sheet.html +++ b/src/components/info-sheet/info-sheet.html @@ -10,12 +10,33 @@ Needs Backup - Congratulations! It looks like you've recently received money. It's highly advised that you verify your recovery phrase so that your funds can be recovered in case your phone was lost or stolen. + Congratulations! It looks like you've recently received money. It's highly advised that you verify your recovery + phrase so that your funds can be recovered in case your phone was lost or stolen. Continue Not right now + + No address for testnet + {{params.coin}} + + {{params.payId}} has not yet configured a receiving address for testnet {{params.coin}}. + + Ok + + + + Are you sure? + + The payment information for {{params.payIdDetails.payId}} cannot be verified. ONLY send to this + address IF you TRUST the server. + + Continue + No + + Miner Fee Notice @@ -70,16 +91,19 @@ Do this later? - Receiving funds will be temporary disabled until you can verify your recovery phrase has been stored away securely. You can always complete this later from your wallet settings. + Receiving funds will be temporary disabled until you can verify your recovery phrase has been stored away + securely. You can always complete this later from your wallet settings. Ok Continue Backup - {{'Your {walletType} wallet is backed up!' | translate : {walletType: params.walletType} }} + {{'Your {walletType} wallet is backed up!' | translate : {walletType: params.walletType} }} - Be sure to store your recovery phrase in a secure place. If this app is deleted, your money cannot be recovered without it. + Be sure to store your recovery phrase in a secure place. If this app is deleted, your money cannot be recovered + without it. GOT IT @@ -87,7 +111,8 @@ Safeguard your recovery phrase - Your recovery phrase is composed of 12 randomly selected words. Please carefully write down each word in the order they appear. + Your recovery phrase is composed of 12 randomly selected words. Please carefully write down each word in the + order they appear. GOT IT @@ -95,7 +120,8 @@ Sensitive Data - The information you are about to share/export may contain sensitive data such us wallet IDs, addresses, balances, etc. Please be sure you trust the person or entity with whom you are sharing this information. + The information you are about to share/export may contain sensitive data such us wallet IDs, addresses, + balances, etc. Please be sure you trust the person or entity with whom you are sharing this information. I understand Go Back @@ -116,17 +142,25 @@ - Select an Ethereum Wallet - Ethereum wallet required - Ethereum testnet wallet required - Which Ethereum wallet would you like this token to use for transaction costs? - Which Ethereum wallet would you like this multisig to use for transaction costs? This feature is only available for the KOVAN TESTNET. ETH Multisig wallets for MAINNET are not yet allowed. + Select an Ethereum + Wallet + Ethereum wallet + required + Ethereum testnet wallet + required + Which + Ethereum wallet would you like this token to use for transaction costs? + Which + Ethereum wallet would you like this multisig to use for transaction costs? This feature is only available for + the KOVAN TESTNET. ETH Multisig wallets for MAINNET are not yet allowed. - In order to create a token wallet, an Ethereum (ETH) wallet is required. Please create an ETH wallet first. Only one {{ params.token.name }} wallet is allowed per ETH wallet. + In order to create a token wallet, an Ethereum (ETH) wallet is required. Please create an ETH wallet first. Only + one {{ params.token.name }} wallet is allowed per ETH wallet. - In order to create this multisig wallet, an Ethereum (ETH) testnet wallet is required. This feature is only available for the KOVAN TESTNET. ETH Multisig wallets for MAINNET are not yet allowed. + In order to create this multisig wallet, an Ethereum (ETH) testnet wallet is required. This feature is only + available for the KOVAN TESTNET. ETH Multisig wallets for MAINNET are not yet allowed. @@ -154,13 +188,15 @@ GOT IT - OPEN IN BROWSER + OPEN IN BROWSER {{'Archive All {brand} Gift Cards?' | translate: params }} - {{'Have you redeemed all of your {brand} Gift Cards? Archived gift cards will still be accessible in settings.' | translate: params }} + {{'Have you redeemed all of your {brand} Gift Cards? Archived gift cards will still be accessible in settings.' | translate: params }} YES @@ -184,7 +220,8 @@ Activation Fee - {{'{displayName} gift cards contain an additional activation fee of {fee} {currency}.' | translate: params }} + {{'{displayName} gift cards contain an additional activation fee of {fee} {currency}.' | translate: params }} OK @@ -196,7 +233,8 @@ Below Minimum Amount - {{'The purchase amount must be at least {minAmount} {currency}. Please modify your amount.' | translate: params }} + {{'The purchase amount must be at least {minAmount} {currency}. Please modify your amount.' | translate: params }} GOT IT @@ -209,9 +247,11 @@ - {{'{appName} BCH wallets use the CashAddr format by default' | translate: {appName:params.appName} }} + {{'{appName} BCH wallets use the CashAddr format by default' | translate: {appName:params.appName} }} - If you need to send to "old" addresses (like the one you just pasted), and you are SURE those are BCH addresses, you can "translate" them to the corresponding address in CashAddr format. + If you need to send to "old" addresses (like the one you just pasted), and you are SURE those are BCH addresses, + you can "translate" them to the corresponding address in CashAddr format. Translate address GOT IT @@ -220,7 +260,8 @@ We appreciate your review - 5-star ratings help us get BitPay Wallet into more hands, and more users means more resources can be committed to the app! + 5-star ratings help us get BitPay Wallet into more hands, and more users means more resources can be committed + to the app! {{'Continue to {storeName}' | translate: {storeName: params?.storeName} }} @@ -229,7 +270,8 @@ Incorrect backup phrase - It's important that you write your recovery phrase down correctly. If something happens to your wallet, you'll need it to recover your money. Please review and try again. + It's important that you write your recovery phrase down correctly. If something happens to your wallet, you'll + need it to recover your money. Please review and try again. START OVER @@ -251,7 +293,8 @@ - {{params.countryCode === 'US' ? 'U.S.' : params.countryCode}} Phone Required + {{params.countryCode === 'US' ? 'U.S.' : params.countryCode}} Phone + Required {{'Only a {name} phone number can be used for this purchase.' | translate: {name:params.country.name} }} @@ -286,7 +329,8 @@ Report an issue - Found something broken? Please report it as a bug. If you need immediate help with a problem you are having, then get help by contacting our support team. + Found something broken? Please report it as a bug. If you need immediate help with a problem you are having, + then get help by contacting our support team. Get help Report issue @@ -295,7 +339,8 @@ Store this key separately - This is a new 12-word key that manages its own set of wallets. It is unrelated to any 12 word key you may have previously created. Store and manage it accordingly. + This is a new 12-word key that manages its own set of wallets. It is unrelated to any 12 word key you may have + previously created. Store and manage it accordingly. Learn More GOT IT @@ -304,7 +349,8 @@ Warning! - Legacy address types in Bitcoin Cash are OBSOLETE. Only use this feature if you are required to provide a legacy address to the sending party + Legacy address types in Bitcoin Cash are OBSOLETE. Only use this feature if you are required to provide a legacy + address to the sending party I Understand Cancel @@ -313,7 +359,8 @@ Warning! - Sending BTC to a Bitcoin Cash legacy address will result on permanent lost of funds. This feature is only for advanced users. + Sending BTC to a Bitcoin Cash legacy address will result on permanent lost of funds. This feature is only for + advanced users. Cancel Show me the legacy address @@ -334,7 +381,8 @@ Keep in mind your encrypt password is not the 12-word recovery phrase
  • - You can always reset your encrypt password on your key settings under the option Clear Encrypt Password using your 12 words recovery phrase + You can always reset your encrypt password on your key settings under the option Clear Encrypt + Password using your 12 words recovery phrase
  • @@ -353,8 +401,11 @@ RBF transaction - Be careful. Until it confirms, the transaction could be replaced/redirected by the sender. You can try to speed it up by using a higher fee. - Read more + Be careful. Until it confirms, the transaction could be replaced/redirected by the sender. You + can try to speed it up by using a higher fee. + Read more Speed up transaction View details @@ -372,7 +423,9 @@ {{ 'Subtotal' | translate }} {{ 'For BitPay invoices and gift cards the subtotal amount is the product or service amount plus network costs.' | translate }} - Read more + Read more GOT IT @@ -381,7 +434,9 @@ {{ 'Total' | translate }} {{ 'The total amount is the subtotal amount plus transaction fees.' | translate }} - Read more + Read more GOT IT
    @@ -423,7 +478,8 @@ No wallet available - You do not have a {{params.coin ? params.coin : ''}} wallet able to receive funds. If you decide to create it, remember it needs to be backed up. + You do not have a {{params.coin ? params.coin : ''}} wallet able to receive funds. If you decide to create it, + remember it needs to be backed up. Create Wallet GOT IT diff --git a/src/pages/send/send.html b/src/pages/send/send.html index 7dc41869feb..45d85c82dcb 100644 --- a/src/pages/send/send.html +++ b/src/pages/send/send.html @@ -31,15 +31,18 @@
    - - + +
    - +
    diff --git a/src/pages/send/send.ts b/src/pages/send/send.ts index 3568899b8b9..b4be51bba0b 100644 --- a/src/pages/send/send.ts +++ b/src/pages/send/send.ts @@ -14,10 +14,18 @@ import { ErrorsProvider } from '../../providers/errors/errors'; import { IncomingDataProvider } from '../../providers/incoming-data/incoming-data'; import { Logger } from '../../providers/logger/logger'; import { OnGoingProcessProvider } from '../../providers/on-going-process/on-going-process'; +import { + getAddressFromPayId, + getPayIdUrl, + isPayId, + PayIdDetails +} from '../../providers/pay-id/pay-id'; import { PayproProvider } from '../../providers/paypro/paypro'; import { ProfileProvider } from '../../providers/profile/profile'; // Pages +import { HttpClient } from '@angular/common/http'; +import { InfoSheetComponent } from '../../components/info-sheet/info-sheet'; import { CopayersPage } from '../add/copayers/copayers'; import { ImportWalletPage } from '../add/import-wallet/import-wallet'; import { JoinWalletPage } from '../add/join-wallet/join-wallet'; @@ -44,6 +52,7 @@ export class SendPage { public search: string = ''; public hasWallets: boolean; public invalidAddress: boolean; + public confirmPayIdSheet: InfoSheetComponent; private validDataTypeMap: string[] = [ 'BitcoinAddress', 'BitcoinCashAddress', @@ -73,6 +82,7 @@ export class SendPage { constructor( private currencyProvider: CurrencyProvider, + private http: HttpClient, private navCtrl: NavController, private navParams: NavParams, private payproProvider: PayproProvider, @@ -183,8 +193,8 @@ export class SendPage { return false; } - private redir() { - this.incomingDataProvider.redir(this.search, { + private redir(search?: string) { + this.incomingDataProvider.redir(search || this.search, { activePage: 'SendPage', amount: this.navParams.data.amount, coin: this.navParams.data.coin // TODO ???? what is this for ? @@ -226,8 +236,33 @@ export class SendPage { this.invalidAddress = false; } + public async handlePayId() { + if (this.confirmPayIdSheet) { + this.confirmPayIdSheet.onDidDismiss(() => {}); + await this.confirmPayIdSheet.dismiss(); + } + this.invalidAddress = false; + const payIdDetails = await this.fetchPayIdDetails(this.search); + const address = getAddressFromPayId(payIdDetails, { + coin: this.wallet.coin, + network: this.wallet.network + }); + return address + ? this.showConfirmPayIdSheet({ payIdDetails }) + : this.showPayIdUnsupportedCoinSheet({ + payId: this.search, + coin: this.wallet.coin.toUpperCase(), + network: this.wallet.network + }); + } + public async processInput() { if (this.search == '') this.invalidAddress = false; + if (isPayId(this.search)) { + return this.handlePayId().catch(() => { + this.invalidAddress = true; + }); + } const hasContacts = await this.checkIfContact(); if (!hasContacts) { const parsedData = this.incomingDataProvider.parseData(this.search); @@ -276,7 +311,6 @@ export class SendPage { const isValid = this.checkCoinAndNetwork(this.search); if (isValid) this.redir(); } else if (parsedData && parsedData.type == 'BitPayCard') { - // this.close(); this.incomingDataProvider.redir(this.search, { activePage: 'SendPage' }); @@ -306,6 +340,47 @@ export class SendPage { ); } + public async fetchPayIdDetails(payId: string): Promise { + const url = getPayIdUrl(payId); + return this.http + .get(url, { + headers: { + 'PayID-Version': '1.0', + Accept: 'application/payid+json' + } + }) + .toPromise() as Promise; + } + + public showPayIdUnsupportedCoinSheet(params: { + payId: string; + coin: string; + network: string; + }): void { + this.invalidAddress = true; + const infoSheet = this.actionSheetProvider.createInfoSheet( + 'pay-id-unsupported-coin', + params + ); + infoSheet.present(); + } + + public showConfirmPayIdSheet(params: { payIdDetails: PayIdDetails }): void { + this.confirmPayIdSheet = this.actionSheetProvider.createInfoSheet( + 'pay-id-confirmation', + params + ); + this.confirmPayIdSheet.present(); + this.confirmPayIdSheet.onDidDismiss(option => { + this.confirmPayIdSheet = undefined; + if (option) { + this.redir(getAddressFromPayId(params.payIdDetails, this.wallet)); + } else { + this.search = ''; + } + }); + } + public showMoreOptions(): void { const optionsSheet = this.actionSheetProvider.createOptionsSheet( 'send-options', diff --git a/src/providers/action-sheet/action-sheet.ts b/src/providers/action-sheet/action-sheet.ts index db7848f54d2..1c7011e1c7a 100644 --- a/src/providers/action-sheet/action-sheet.ts +++ b/src/providers/action-sheet/action-sheet.ts @@ -40,6 +40,8 @@ export type InfoSheetType = | 'miner-fee' | 'miner-fee-notice' | 'one-phone-country' + | 'pay-id-confirmation' + | 'pay-id-unsupported-coin' | 'payment-request' | 'payment-method-changed' | 'print-required' diff --git a/src/providers/pay-id/pay-id.ts b/src/providers/pay-id/pay-id.ts new file mode 100644 index 00000000000..be39c6b92da --- /dev/null +++ b/src/providers/pay-id/pay-id.ts @@ -0,0 +1,41 @@ +export interface PayIdAddress { + paymentNetwork: string; + environment: string; + addressDetailsType: string; + addressDetails: { address: string }; +} + +export interface PayIdDetails { + payId: string; + version: string; + addresses: PayIdAddress[]; +} + +export function isPayId(value: string): boolean { + return ( + value.includes('$') && + !value.startsWith('$') && + !value.includes('https://') && + value.endsWith('ematiu.sandbox.payid.org') + ); +} + +export function getPayIdUrl(payId: string): string { + const parts = payId.split('$'); + return `https://${parts[1]}/${parts[0]}`; +} + +export function getAddressFromPayId( + payIdDetails: PayIdDetails, + params: { + coin: string; + network: string; + } +): string | undefined { + const address = payIdDetails.addresses.find( + address => + address.paymentNetwork === params.coin.toUpperCase() && + address.environment === params.network.toUpperCase() + ); + return address && address.addressDetails.address; +}