stone_deep_tech 1.0.0
stone_deep_tech: ^1.0.0 copied to clipboard
Plugin Flutter para integração com Deeplink da Stone para meios de pagamento.
Stone Deep Tech #
🚨 Aviso #
Este plugin é não oficial e foi desenvolvido de forma independente para facilitar a integração de pagamentos via Deeplink da Stone em dispositivos Android Smart POS.
Plugin Flutter para integração com o sistema de Deeplinks da Stone para meios de pagamento, impressão e reimpressão de comprovantes.
📋 Descrição #
O Stone Deep Tech é um plugin Flutter que permite que desenvolvedores integrem seus aplicativos com o sistema de pagamento da Stone através de deeplinks, sem precisar lidar diretamente com código nativo Android. O plugin oferece uma interface simples e intuitiva para realizar operações de pagamento, cancelamento, impressão customizada e reimpressão de comprovantes.
✨ Funcionalidades #
- ✅ Pagamento: Envio de deeplinks para processamento de pagamentos
- ✅ Cancelamento: Cancelamento de transações através de deeplink
- ✅ Impressão Customizada: Impressão de conteúdo personalizado (texto, linhas, imagens)
- ✅ Reimpressão: Reimpressão de comprovantes de transações anteriores
- ✅ Validação de Licença: Sistema integrado de validação de licença
- ✅ Callbacks Assíncronos: Recebimento de respostas de forma assíncrona
- ✅ Type Safety: Enums para maior segurança de tipos
- ✅ Suporte Completo: DEBIT, CREDIT, VOUCHER, INSTANT_PAYMENT e PIX
📦 Requisitos #
- Flutter SDK: >=3.0.0
- Android: SDK mínimo 24 (Android 7.0)
- Aplicativo Stone instalado no dispositivo
🚀 Instalação #
Adicione o plugin ao seu pubspec.yaml:
dependencies:
stone_deep_tech:
path: ../stone_deep_tech # ou use a versão do pub.dev
Execute:
flutter pub get
⚙️ Configuração Android #
1. Configurar o AndroidManifest.xml #
Adicione o intent-filter necessário para receber respostas de deeplink no seu AndroidManifest.xml:
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
...>
<!-- ... outros intent-filters ... -->
<!-- Intent filter para receber respostas de deeplink da Stone -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="pay-response"
android:scheme="@string/return_scheme" />
</intent-filter>
</activity>
Importante: O launchMode="singleTop" é necessário para garantir que o app não crie múltiplas instâncias ao receber deeplinks.
2. Configurar o Scheme de Retorno #
Crie ou atualize o arquivo res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="return_scheme">seu_app_scheme</string>
</resources>
Substitua seu_app_scheme pelo scheme único do seu aplicativo (ex: meuapp, stone_deep_tech_example).
3. Configurar as Chaves de Licença #
O plugin requer chaves de licença para funcionar. Configure-as de uma das seguintes formas:
Opção A: Variáveis de Ambiente
export LICENCE_KEY="sua_chave_aqui"
export LICENCE_INTERNAL_KEY="aHR0cHM6Ly9wb3MtcGF5bWVudHMtYXBpLTU3NzQ2NDIzNTQwOC5zb3V0aGFtZXJpY2EtZWFzdDEucnVuLmFwcC9wb3MtcGF5bWVudHMvbGljZW5jZS9jaGVjay9pbnZvaWNl"
Opção B: Arquivo local.properties
Crie ou edite o arquivo android/local.properties na raiz do projeto do plugin:
licenceKey=sua_chave_aqui
licenceInternalKey=aHR0cHM6Ly9wb3MtcGF5bWVudHMtYXBpLTU3NzQ2NDIzNTQwOC5zb3V0aGFtZXJpY2EtZWFzdDEucnVuLmFwcC9wb3MtcGF5bWVudHMvbGljZW5jZS9jaGVjay9pbnZvaWNl
Importante: O arquivo local.properties geralmente está no .gitignore e não deve ser commitado no controle de versão.
4. Validação de Licença #
O plugin valida automaticamente a licença antes de permitir qualquer operação. Se a licença não estiver válida ou não estiver configurada, todas as chamadas de método retornarão um erro com código LICENCE_ERROR.
Como funciona:
- Primeiro verifica se existe uma licença válida armazenada localmente (criptografada)
- Se não existir ou estiver expirada, faz uma requisição ao servidor de licenciamento
- A licença é armazenada localmente de forma criptografada por até 7 dias
- Após 7 dias, a validação é feita novamente contra o servidor
- Se a validação falhar, todas as operações são bloqueadas
📖 Como Usar #
1. Inicializar o Plugin #
Primeiro, crie um handler para receber os callbacks:
import 'package:stone_deep_tech/stone_deep_tech.dart';
class MyDeepLinkHandler implements IDeepLinkHandler {
@override
Future<void> onDeeplinkResponse(String responseUri) async {
print('Resposta recebida: $responseUri');
// Processe a resposta aqui
// Exemplo: extrair código de resultado, ATK, etc.
}
@override
Future<void> onError(String message) async {
print('Erro: $message');
// Trate erros aqui
}
@override
Future<void> onDeeplinkSent() async {
print('Deeplink enviado com sucesso');
}
}
// Inicialize o plugin
final handler = MyDeepLinkHandler();
StoneDeepTech.I.initDeeplink(handler: handler);
2. Enviar Pagamento #
import 'package:stone_deep_tech/stone_deep_tech.dart';
final params = DeeplinkParams(
amount: 10000, // R$ 100,00 em centavos
editableAmount: false, // Não permite editar valor (padrão)
transactionType: TransactionType.debit, // DEBIT, CREDIT, VOUCHER, INSTANT_PAYMENT, PIX
returnScheme: 'seu_app_scheme', // Deve corresponder ao strings.xml
// Parâmetros opcionais:
installmentCount: 3, // Número de parcelas (2 a 99)
installmentType: InstallmentType.merchant, // MERCHANT, ISSUER, ou NONE
orderId: 12345, // ID do pedido
);
final success = await StoneDeepTech.I.deeplink.sendDeeplink(params);
if (success) {
print('Deeplink de pagamento enviado!');
} else {
print('Erro ao enviar deeplink');
}
Nota: O campo installmentType só é enviado quando transactionType é credit.
3. Cancelar Transação #
import 'package:stone_deep_tech/stone_deep_tech.dart';
final cancelParams = CancelParams(
returnScheme: 'seu_app_scheme',
atk: 'codigo_atk_da_transacao', // Código único da transação gerado pelo autorizador
amount: 10000, // Valor em centavos (opcional)
editableAmount: false, // Não permite editar valor (padrão)
);
final success = await StoneDeepTech.I.deeplink.sendCancel(cancelParams);
if (success) {
print('Deeplink de cancelamento enviado!');
}
4. Impressão Customizada #
import 'package:stone_deep_tech/stone_deep_tech.dart';
import 'dart:convert';
// Criar conteúdo de impressão
final printContent = [
// Texto customizado
TextPrintContent(
content: 'LOJA EXEMPLO',
align: PrintTextAlign.center,
size: PrintTextSize.big,
),
// Linha simples
LinePrintContent(content: '--------------------------------'),
// Texto alinhado à esquerda
TextPrintContent(
content: 'Produto: Item 1',
align: PrintTextAlign.left,
size: PrintTextSize.small,
),
// Texto alinhado à direita
TextPrintContent(
content: 'R\$ 100,00',
align: PrintTextAlign.right,
size: PrintTextSize.medium,
),
// Imagem (Base64)
ImagePrintContent(
imagePath: 'iVBORw0KGgoAAAANSUhEUgAA...', // String Base64
),
];
final printParams = PrintParams(
schemeReturn: 'seu_app_scheme',
printableContent: printContent,
showFeedbackScreen: true, // Mostrar tela de sucesso/erro
);
final success = await StoneDeepTech.I.deeplink.sendPrint(printParams);
if (success) {
print('Deeplink de impressão enviado!');
}
5. Reimpressão de Comprovante #
import 'package:stone_deep_tech/stone_deep_tech.dart';
final reprintParams = ReprintParams(
schemeReturn: 'seu_app_scheme',
atk: 'codigo_atk_da_transacao', // Código único da transação
typeCustomer: TypeCustomer.client, // CLIENT ou MERCHANT
showFeedbackScreen: true, // Mostrar tela de sucesso/erro
);
final success = await StoneDeepTech.I.deeplink.sendReprint(reprintParams);
if (success) {
print('Deeplink de reimpressão enviado!');
}
6. Processar Resultados de Impressão #
import 'package:stone_deep_tech/stone_deep_tech.dart';
class MyDeepLinkHandler implements IDeepLinkHandler {
@override
Future<void> onDeeplinkResponse(String responseUri) async {
// Verificar se é um resultado de impressão
final printResult = DeeplinkResponseParser.parsePrintResult(responseUri);
if (printResult != null) {
switch (printResult) {
case PrintResult.success:
print('Impressão realizada com sucesso!');
break;
case PrintResult.printerOutOfPaper:
print('Impressora sem papel ou com tampa aberta');
break;
case PrintResult.printerInitError:
print('Erro ao inicializar impressora');
break;
// ... outros casos
default:
print('Erro: ${printResult.description}');
}
} else {
// Processar outros tipos de resposta (pagamento, cancelamento, etc.)
print('Resposta recebida: $responseUri');
}
}
// ... outros métodos
}
📚 API Completa #
StoneDeepTech #
Classe principal do plugin, singleton.
Métodos
static StoneDeepTech get I- Retorna a instância singletonDeeplink initDeeplink({required IDeepLinkHandler handler})- Inicializa o plugin com um handler
DeeplinkParams #
Parâmetros para envio de pagamento.
Propriedades
| Propriedade | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
amount |
int? |
Não | - | Valor em centavos (ex: 10000 = R$ 100,00) |
editableAmount |
bool |
Não | false |
Permite editar valor no app de pagamento |
transactionType |
TransactionType? |
Não | - | DEBIT, CREDIT, VOUCHER, INSTANT_PAYMENT, PIX |
installmentCount |
int? |
Não | - | Número de parcelas (2 a 99) |
installmentType |
InstallmentType? |
Não | NONE |
MERCHANT, ISSUER, ou NONE |
orderId |
int? |
Não | - | ID do pedido para rastreamento |
returnScheme |
String |
Sim | - | Scheme do app para receber resposta |
Enums
TransactionType:
debit- Débitocredit- Créditovoucher- VoucherinstantPayment- Pagamento instantâneopix- PIX
InstallmentType:
merchant- Parcelado sem jurosissuer- Parcelado com jurosnone- À vista (padrão)
CancelParams #
Parâmetros para cancelamento.
Propriedades
| Propriedade | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
returnScheme |
String |
Sim | - | Scheme do app para receber resposta |
atk |
String |
Sim | - | Código único da transação |
amount |
int? |
Não | - | Valor em centavos |
editableAmount |
bool |
Não | false |
Permite editar valor |
PrintParams #
Parâmetros para impressão customizada.
Propriedades
| Propriedade | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
schemeReturn |
String |
Sim | - | Scheme do app para receber resposta |
printableContent |
List<PrintContent> |
Sim | - | Lista de conteúdo para impressão |
showFeedbackScreen |
bool |
Não | true |
Mostrar tela de sucesso/erro |
Tipos de Conteúdo
TextPrintContent:
content(String) - Texto a ser impressoalign(PrintTextAlign) - center, right, leftsize(PrintTextSize) - big, medium, small
LinePrintContent:
content(String) - Texto sem customização
ImagePrintContent:
imagePath(String) - Imagem em Base64
ReprintParams #
Parâmetros para reimpressão.
Propriedades
| Propriedade | Tipo | Obrigatório | Padrão | Descrição |
|---|---|---|---|---|
schemeReturn |
String |
Sim | - | Scheme do app para receber resposta |
atk |
String |
Sim | - | Código único da transação |
typeCustomer |
TypeCustomer |
Sim | - | CLIENT ou MERCHANT |
showFeedbackScreen |
bool |
Não | true |
Mostrar tela de sucesso/erro |
Enums
TypeCustomer:
client- Comprovante do clientemerchant- Comprovante do lojista
PrintResult #
Resultados possíveis de impressão/reimpressão.
Valores
| Resultado | Código | Descrição |
|---|---|---|
success |
SUCCESS | Impressão realizada com sucesso |
printerOutOfPaper |
PRINTER_OUT_OF_PAPER | Impressora sem papel ou com tampa aberta |
printerInitError |
PRINTER_INIT_ERROR | Erro ao inicializar impressora |
printerLowEnergy |
PRINTER_LOW_ENERGY | Máquina com baixa energia |
printerBusy |
PRINTER_BUSY | Impressora ocupada |
printerUnsupportedFormat |
PRINTER_UNSUPPORTED_FORMAT | Formato não suportado |
printerInvalidData |
PRINTER_INVALID_DATA | Limite máximo do buffer ultrapassado |
printerOverheating |
PRINTER_OVERHEATING | Superaquecimento da impressora |
printerPaperJam |
PRINTER_PAPER_JAM | Papel preso na caixa de bobina |
printerPrintError |
PRINTER_PRINT_ERROR | Erro genérico da impressora |
IDeepLinkHandler #
Interface para receber callbacks do plugin.
Métodos
Future<void> onDeeplinkResponse(String responseUri)- Chamado quando uma resposta é recebidaFuture<void> onError(String message)- Chamado quando ocorre um erroFuture<void> onDeeplinkSent()- Chamado quando o deeplink é enviado
💡 Exemplos Completos #
Exemplo 1: Pagamento Simples #
import 'package:flutter/material.dart';
import 'package:stone_deep_tech/stone_deep_tech.dart';
class PaymentPage extends StatefulWidget {
@override
_PaymentPageState createState() => _PaymentPageState();
}
class _PaymentPageState extends State<PaymentPage> implements IDeepLinkHandler {
bool _isLoading = false;
@override
void initState() {
super.initState();
StoneDeepTech.I.initDeeplink(handler: this);
}
Future<void> processPayment() async {
setState(() => _isLoading = true);
final params = DeeplinkParams(
amount: 10000, // R$ 100,00
editableAmount: false,
transactionType: TransactionType.debit,
returnScheme: 'meuapp',
);
await StoneDeepTech.I.deeplink.sendDeeplink(params);
}
@override
Future<void> onDeeplinkResponse(String responseUri) async {
setState(() => _isLoading = false);
print('Resposta: $responseUri');
// Processar resposta...
}
@override
Future<void> onError(String message) async {
setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Erro: $message')),
);
}
@override
Future<void> onDeeplinkSent() async {
print('Deeplink enviado');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Pagamento')),
body: Center(
child: ElevatedButton(
onPressed: _isLoading ? null : processPayment,
child: _isLoading
? CircularProgressIndicator()
: Text('Pagar R\$ 100,00'),
),
),
);
}
}
Exemplo 2: Impressão com Tratamento de Erros #
import 'package:stone_deep_tech/stone_deep_tech.dart';
class PrintHandler implements IDeepLinkHandler {
@override
Future<void> onDeeplinkResponse(String responseUri) async {
final printResult = DeeplinkResponseParser.parsePrintResult(responseUri);
if (printResult != null) {
if (printResult == PrintResult.success) {
print('✅ Impressão realizada com sucesso!');
} else {
print('❌ Erro na impressão: ${printResult.description}');
// Exibir mensagem de erro ao usuário
}
}
}
@override
Future<void> onError(String message) async {
print('Erro: $message');
if (message.contains('LICENCE_ERROR')) {
print('Licença inválida ou não configurada');
}
}
@override
Future<void> onDeeplinkSent() async {
print('Enviando impressão...');
}
}
void printReceipt() {
final handler = PrintHandler();
StoneDeepTech.I.initDeeplink(handler: handler);
final content = [
TextPrintContent(
content: 'COMPROVANTE DE VENDA',
align: PrintTextAlign.center,
size: PrintTextSize.big,
),
LinePrintContent(content: '--------------------------------'),
TextPrintContent(
content: 'Valor: R\$ 100,00',
align: PrintTextAlign.left,
size: PrintTextSize.medium,
),
];
final params = PrintParams(
schemeReturn: 'meuapp',
printableContent: content,
showFeedbackScreen: true,
);
StoneDeepTech.I.deeplink.sendPrint(params);
}
🔍 Tratamento de Erros #
Erros de Licença #
try {
await StoneDeepTech.I.deeplink.sendDeeplink(params);
} catch (e) {
if (e.toString().contains('LICENCE_ERROR')) {
// Licença inválida ou não configurada
print('Configure as chaves de licença no local.properties');
}
}
Erros de Validação #
O plugin valida automaticamente:
- Licença configurada
- Licença válida no servidor
- Parâmetros obrigatórios presentes
Erros de Impressão #
Use o enum PrintResult para identificar erros específicos:
final printResult = DeeplinkResponseParser.parsePrintResult(responseUri);
if (printResult == PrintResult.printerOutOfPaper) {
// Informar usuário sobre falta de papel
}
🐛 Troubleshooting #
Problema: Deeplink não abre o app Stone #
Solução:
- Verifique se o aplicativo Stone está instalado
- Verifique se o
returnSchemeestá configurado corretamente - Verifique os logs do Android para erros
Problema: Resposta não é recebida #
Solução:
- Verifique se o
intent-filterestá configurado noAndroidManifest.xml - Verifique se o
launchMode="singleTop"está configurado - Verifique se o
returnSchemecorresponde aostrings.xml - Verifique se o handler está implementado corretamente
Problema: Erro de licença #
Solução:
- Verifique se as chaves estão configuradas em
local.propertiesou variáveis de ambiente - Verifique se as chaves são válidas
- Verifique a conexão com a internet (primeira validação)
- Verifique os logs para mensagens de erro específicas
Problema: Parcelamento não funciona #
Solução:
- Verifique se
transactionTypeécredit - Verifique se
installmentCountestá entre 2 e 99 - Lembre-se:
installmentTypesó é enviado quandotransactionTypeécredit
📁 Estrutura do Projeto #
lib/
├── src/
│ ├── deeplink/
│ │ └── deeplink.dart # Classe principal de deeplink
│ ├── interface/
│ │ ├── handler/
│ │ │ └── ideeplink_handler.dart # Interface de handler
│ │ └── model/
│ │ ├── deeplink_params.dart # Parâmetros de pagamento
│ │ ├── cancel_params.dart # Parâmetros de cancelamento
│ │ ├── print_params.dart # Parâmetros de impressão
│ │ ├── print_result.dart # Resultados de impressão
│ │ └── reprint_params.dart # Parâmetros de reimpressão
│ └── licence/
│ ├── constants/
│ │ └── licence_constants.dart # Constantes de licença
│ └── licence.dart # Exportação do módulo
└── stone_deep_tech.dart # Classe principal do plugin
📝 Notas Importantes #
- Validação de Licença: Todas as operações são bloqueadas se a licença não for válida
- Thread Safety: O plugin é thread-safe e pode ser usado de qualquer thread
- Singleton: O plugin usa o padrão singleton, então sempre use
StoneDeepTech.I - Callbacks: Os callbacks são assíncronos e podem ser chamados de qualquer thread
- Parcelamento:
installmentTypesó é enviado quandotransactionTypeécredit - Impressão: Imagens devem estar em formato Base64
- Valores: Todos os valores monetários são em centavos (inteiros)
🔗 Referências #
📄 Licença #
Veja o arquivo LICENSE para mais detalhes.
📞 Suporte #
Para suporte, abra uma issue no repositório do projeto.
Desenvolvido com ❤️ para facilitar a integração com Stone Payment