Feat(P3): Modularize module and add Pardakht Novin support

- Split `p3` module into `p3_pardakht_novin` and `p3_sepehr` sub-modules
- Create `pardakht_novin_app` module with dedicated WebView and Hilt configuration
- Rename P3 services, printers, and models to be vendor-specific (e.g., `P3PardakhtNovinService`)
- Add `p3_pardakht_novin` and `p3_sepehr` product flavors to `tis_app` and `stage_app`
- Update project settings and build configurations to include the new modular structure
- Implement Sepehr-specific POS and Printer services in the new module
This commit is contained in:
Amir Mousavi
2026-06-16 00:18:45 +03:30
parent 1246a35771
commit 5d60f62c20
85 changed files with 2031 additions and 64 deletions
@@ -5,7 +5,7 @@ plugins {
}
android {
namespace = "com.example.p3"
namespace = "com.example.p3_pardakht_novin"
compileSdk {
version = release(36) {
minorApiLevel = 1
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import android.content.Context
import android.content.Intent
@@ -6,15 +6,16 @@ import android.content.pm.PackageManager
import androidx.activity.result.ActivityResult
import com.example.core.model.PaymentResultEntity
import com.example.core.model.PaymentResultStatus
import com.example.p3.models.IapAccountSplit
import com.example.p3_pardakht_novin.models.IapAccountSplit
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class IapPosService @Inject constructor(
private val context: Context
class P3PardakhtNovinIapPosService @Inject constructor(
@param:ApplicationContext private val context: Context
) {
private val gson = Gson()
@@ -0,0 +1,335 @@
package com.example.p3_pardakht_novin
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.util.Log
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.graphics.createBitmap
import com.example.core.domain.Printer
import com.example.core.model.PrintEntity
import com.pos.sdk.printer.POIPrinterManager
import com.pos.sdk.printer.POIPrinterManager.IPrinterListener
import com.pos.sdk.printer.models.BitmapPrintLine
import com.pos.sdk.printer.models.PrintLine
import com.pos.sdk.printer.models.TextPrintLine
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
import javax.inject.Inject
class P3PardakhtNovinPrinter @Inject constructor(
@param:ApplicationContext private val context: Context,
) : Printer {
companion object {
private const val TAG = "Printer"
// Printer state constants (should ideally come from the SDK if available)
private const val PRINTER_STATE_ERROR = 4
private const val DEFAULT_PRINT_GRAY = 2000
private const val DEFAULT_LINE_SPACE = 5
}
/**
* Prints a sample receipt for demonstration.
*/
fun printSampleReceipt(context: Context) {
// Use applicationContext to avoid potential memory leaks
val printerManager = initialP3Printer()
printerManager?.apply {
setPrintGray(DEFAULT_PRINT_GRAY)
setLineSpace(DEFAULT_LINE_SPACE)
// Header
addPrintLine(TextPrintLine("This is an example of a receipt", PrintLine.CENTER))
// Reusable logo bitmap
val logoBitmap = drawableToBitmap(context, android.R.drawable.btn_plus)
val logoLine = logoBitmap?.let { BitmapPrintLine(it, PrintLine.CENTER) }
logoLine?.let { addPrintLine(it) }
// Address
val addressFont = getFontPath("font")
if (addressFont.isNotEmpty()) {
setPrintFont(addressFont)
} else {
setPrintFont("/system/fonts/ComingSoon.ttf")
}
addPrintLine(
TextPrintLine(
"Floor ** , Building **, No.*** LONG DONG Avenue, Pudong New District, Shanghai, China",
PrintLine.LEFT,
20
)
)
// Transaction Details
val detailFont = getFontPath("font")
if (detailFont.isNotEmpty()) {
setPrintFont(detailFont)
} else {
setPrintFont("/system/fonts/DroidSansMono.ttf")
}
// addPrintLine(createPrintRow("24 June 2025", " Assistant 6", "815002", size = 18))
// addPrintLine(createPrintRow("Item", "Quantity", "Price", size = 24, isBold = true))
// addPrintLine(createPrintRow("Tomato", "1", "$2.08", size = 24))
// addPrintLine(createPrintRow("Orange", "1", "$1.06", size = 24))
//
// Footer
addPrintLine(TextPrintLine("Total $3.14", PrintLine.RIGHT))
addPrintLine(TextPrintLine(""))
logoLine?.let { addPrintLine(it) }
addPrintLine(TextPrintLine(""))
addPrintLine(
TextPrintLine(
"Did you know you could have earned Rewards points on this purchase?",
PrintLine.CENTER
)
)
addPrintLine(
TextPrintLine(
"Simply sign up today for a Membership Card!",
PrintLine.CENTER
)
)
logoLine?.let { addPrintLine(it) }
addPrintLine(TextPrintLine(" ", 0, 100))
val listener = object : IPrinterListener {
override fun onStart() {
Log.d(TAG, "Print started")
}
override fun onFinish() {
Log.d(TAG, "Print finished")
close()
}
override fun onError(code: Int, msg: String?) {
Log.e(TAG, "Print error - code: $code, message: $msg")
close()
}
}
beginPrint(listener)
}
}
private fun initialP3Printer(): POIPrinterManager? {
val printerManager = POIPrinterManager(context.applicationContext)
printerManager.open()
val state = printerManager.printerState
Log.d(TAG, "Printer state = $state")
if (state == PRINTER_STATE_ERROR) {
Log.e(TAG, "Printer is in an error state ($state). Aborting print.")
printerManager.close()
return null
}
return printerManager
}
/**
* Creates a row with left, center, and right aligned text.
*/
private fun createPrintRow(
label: String,
value: String,
size: Int,
isBold: Boolean = false
): List<TextPrintLine> {
return if (value.hasPersianLetters()) {
listOf(
TextPrintLine(label, PrintLine.LEFT, size, true),
TextPrintLine(value, PrintLine.RIGHT, size, isBold)
)
} else {
listOf(
TextPrintLine(label, PrintLine.LEFT, size, true),
TextPrintLine(value, PrintLine.LEFT, size, isBold)
)
}
}
private fun getFontPath(resourceName: String): String {
return try {
// Find resource ID by name (e.g., "font") in the "font" resource folder
val resId = context.resources.getIdentifier(resourceName, "font", context.packageName)
if (resId == 0) {
Log.e(TAG, "Font resource not found: $resourceName")
return ""
}
val fileName = "$resourceName.ttf"
val fontFile = File(context.filesDir, fileName)
if (!fontFile.exists()) {
context.resources.openRawResource(resId).use { input ->
fontFile.outputStream().use { output ->
input.copyTo(output)
}
}
}
fontFile.absolutePath
} catch (e: Exception) {
Log.e(TAG, "Error loading font from resources: $resourceName", e)
""
}
}
override fun print(
printEntities: List<PrintEntity>,
@DrawableRes icon: Int
) {
val printerManager = initialP3Printer()
printerManager?.apply {
setPrintGray(DEFAULT_PRINT_GRAY)
setLineSpace(DEFAULT_LINE_SPACE)
// Reusable logo bitmap
val logoBitmap = drawableToBitmap(context, icon, 200)
if (logoBitmap != null) {
val logoLine = BitmapPrintLine(logoBitmap, PrintLine.CENTER)
addPrintLine(logoLine)
addPrintLine(TextPrintLine(" ", 0, 10))
}
val customFontPath = getFontPath("font")
if (customFontPath.isNotEmpty()) {
setPrintFont(customFontPath)
} else {
setPrintFont("/system/fonts/DroidSansMono.ttf") // Fallback
}
val divider = drawableToBitmap(context, com.example.core.R.drawable.horizontal_line_svgrepo_com__1_)
printEntities.forEachIndexed { index, printEntity ->
// Header
printEntity.title?.let {title->
addPrintLine(
TextPrintLine(
title,
if (title.hasPersianLetters()) PrintLine.LEFT else PrintLine.RIGHT
)
)
}
printEntity.items?.let {items->
items.forEach { itemEntity ->
addPrintLine(
createPrintRow(
label = itemEntity.label,
value = itemEntity.value,
size = 16,
)
)
}
}
if (divider != null && printEntities.lastIndex > index) {
val dividerLine = BitmapPrintLine(divider, PrintLine.CENTER)
addPrintLine(TextPrintLine(" ", 0, 10))
addPrintLine(dividerLine)
addPrintLine(TextPrintLine(" ", 0, 10))
}
}
addPrintLine(TextPrintLine(" ", 0, 60))
val listener = object : IPrinterListener {
override fun onStart() {
Log.d(TAG, "Print started")
}
override fun onFinish() {
Log.d(TAG, "Print finished")
close()
}
override fun onError(code: Int, msg: String?) {
Log.e(TAG, "Print error - code: $code, message: $msg")
close()
}
}
beginPrint(listener)
}
}
// private fun fixForPrinter(input: String): String {
// val LRM = '\u200E' // کنترل جهت
//
// return input
// .toPersianDigits() // همونی که قبل دادم
// .replace(Regex("(\\d+)")) { matchResult ->
// "$LRM${matchResult.value}$LRM"
// }
// }
private fun String.hasPersianLetters(): Boolean {
return Regex("[\\u0621-\\u0628\\u062A-\\u063A\\u0641-\\u0642\\u0644-\\u0648\\u067E\\u0686\\u0698\\u06A9\\u06AF\\u06CC\\u0647]").containsMatchIn(
this
)
}
private fun drawableToBitmap(context: Context, @DrawableRes drawableId: Int): Bitmap? {
val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val bitmap = createBitmap(
drawable.intrinsicWidth.coerceAtLeast(1),
drawable.intrinsicHeight.coerceAtLeast(1)
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
private fun drawableToBitmap(
context: Context,
@DrawableRes drawableId: Int,
maxWidth: Int = 300
): Bitmap? {
val drawable = ContextCompat.getDrawable(context, drawableId) ?: return null
val original = if (drawable is BitmapDrawable) {
drawable.bitmap
} else {
val bitmap = createBitmap(
drawable.intrinsicWidth.coerceAtLeast(1),
drawable.intrinsicHeight.coerceAtLeast(1)
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
// 👇 scale با حفظ نسبت
val ratio = maxWidth.toFloat() / original.width
val width = maxWidth
val height = (original.height * ratio).toInt()
return Bitmap.createScaledBitmap(original, width, height, true)
}
}
@@ -0,0 +1,173 @@
package com.example.p3_pardakht_novin
import android.device.PrinterManager
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.Log
import com.google.gson.GsonBuilder
import com.google.gson.JsonElement
import com.google.gson.JsonParser
import javax.inject.Inject
import javax.inject.Singleton
/**
* Helper class for interacting with the POS Printer.
*/
@Singleton
class P3PardakhtNovinPrinterHelper @Inject constructor() {
private var printer: PrinterManager? = null
private val gson = GsonBuilder().setPrettyPrinting().create()
private val textPaint = Paint().apply {
textSize = 24f
color = Color.BLACK
isAntiAlias = true
}
init {
detectPrinter()
}
private fun detectPrinter() {
try {
// Attempt to instantiate the manager
val manager = PrinterManager()
try {
// Test the implementation.
// We use getStatus() to see if it's the system implementation or the stub.
val status = manager.getStatus()
printer = manager
Log.d("PrinterHelper", "PrinterManager initialized. Initial status: $status")
} catch (e: Exception) {
val errorMsg = e.cause?.message ?: e.message ?: ""
Log.w("PrinterHelper", "getStatus() failed with: $errorMsg")
if (errorMsg.contains("stub", ignoreCase = true)) {
Log.e("PrinterHelper", "CRITICAL: The SDK JAR is being bundled in your APK. You must Clean and Rebuild.")
} else {
// If it's not a 'stub' error, it might just be the hardware state
printer = manager
}
}
} catch (e: Throwable) {
Log.e("PrinterHelper", "PrinterManager not available on this device: ${e.message}")
}
}
fun getStatusMessage(code: Int): String {
return when (code) {
0 -> "Printer is healthy"
-1 -> "No paper in printer"
2 -> "Device is overheating"
-3 -> "Battery is too low to print"
else -> "Hardware error code: $code"
}
}
fun printJson(jsonData: String, onStatus: (String) -> Unit) {
try {
val jsonElement: JsonElement = JsonParser.parseString(jsonData)
val prettyJson = gson.toJson(jsonElement)
printLines(prettyJson.split("\n"), onStatus)
} catch (e: Exception) {
onStatus("Invalid JSON data provided.")
}
}
fun printLines(lines: List<String>, onStatus: (String) -> Unit) {
if (printer == null) {
detectPrinter()
}
val currentPrinter = printer
if (currentPrinter == null) {
onStatus("Printer hardware not supported or SDK issue.")
return
}
Log.d("PrinterHelper", "Starting print job...")
var bitmap: Bitmap? = null
try {
currentPrinter.open()
currentPrinter.setupPage(384, -1)
currentPrinter.clearPage()
val lineHeight = 32
val totalHeight = (lines.size * lineHeight) + 40
bitmap = Bitmap.createBitmap(384, totalHeight, Bitmap.Config.ARGB_8888)
bitmap.eraseColor(Color.WHITE)
val canvas = Canvas(bitmap)
var yPos = 30f
for (line in lines) {
canvas.drawText(line, 10f, yPos, textPaint)
yPos += lineHeight
}
currentPrinter.drawBitmap(bitmap, 0, 0)
val printResult = currentPrinter.printPage(0)
if (printResult == 0) {
currentPrinter.paperFeed(20)
onStatus("Print Job Completed Successfully")
} else {
onStatus(getStatusMessage(printResult))
}
} catch (e: Exception) {
onStatus("Printer Error: ${e.localizedMessage}")
Log.e("PrinterHelper", "Unexpected error during printing", e)
} finally {
bitmap?.recycle()
try {
currentPrinter.close()
} catch (e: Throwable) {}
}
}
/*
fun printText(): Int {
val manager = printer
val mCenterPaint = Paint().apply {
textSize = 30f
color = Color.BLACK
}
var mBitmap: Bitmap? = null
manager.setupPage(384, -1)
manager.clearPage()
val mconfig = Bitmap.Config.ARGB_8888
mBitmap = Bitmap.createBitmap(384, 2000, mconfig)
mBitmap.eraseColor(-0x1) // 0xffffffff
val mCanvas = Canvas(mBitmap)
mCanvas.drawText("سلام", 330f, 30f, mCenterPaint)
mCanvas.drawText("حال شما خوب هست", 165f, 60f, mCenterPaint)
mCanvas.drawText("سلام", 330f, 90f, mCenterPaint)
mCanvas.drawText("حال شما خوب هست", 165f, 120f, mCenterPaint)
var mirrored: Bitmap? = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.width, 130)
manager.drawBitmap(mirrored, 0, 0)
val ret = manager.printPage(0)
manager.paperFeed(15)
mBitmap?.let {
it.recycle()
mBitmap = null
}
mirrored?.let {
it.recycle()
mirrored = null
}
return ret
}
*/
}
@@ -0,0 +1,47 @@
package com.example.p3_pardakht_novin
import android.content.Intent
import androidx.activity.result.ActivityResult
import com.example.core.domain.PspService
import com.example.core.model.PaymentResultEntity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
class P3PardakhtNovinService @Inject constructor(
private val p3PardakhtNovinIapPosService: P3PardakhtNovinIapPosService,
private val p3PardakhtNovinPrinterHelper: P3PardakhtNovinPrinterHelper
) : PspService {
private val scope = CoroutineScope(Dispatchers.IO)
override fun pay(amount: Long): Intent {
return p3PardakhtNovinIapPosService.startPurchase(amount)
}
override fun print(lines: List<String>, onMessage: (String) -> Unit) {
p3PardakhtNovinPrinterHelper.printLines(lines) { status ->
onMessage("P3 Printer: $status")
}
}
override fun decodePosResponse(
id: String?,
activityResult: ActivityResult
): PaymentResultEntity {
return try {
p3PardakhtNovinIapPosService.decodeResponse(
id = id,
activityResult = activityResult
)
} catch (e: Exception) {
e.printStackTrace()
PaymentResultEntity.failure(
id = id,
e.message
)
}
}
}
@@ -0,0 +1,11 @@
package com.example.p3_pardakht_novin.di
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@Module
@InstallIn(SingletonComponent::class)
object P3PardakhtNovinModule {
// P3PardakhtNovinIapPosService is provided via @Inject constructor
}
@@ -1,4 +1,4 @@
package com.example.p3.models
package com.example.p3_pardakht_novin.models
data class IapAccountSplit(
val index: Int,
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import org.junit.Test
+1
View File
@@ -0,0 +1 @@
/build
+49
View File
@@ -0,0 +1,49 @@
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
}
android {
namespace = "com.example.p3_sepehr"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
defaultConfig {
minSdk = 24
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
implementation(project(":core"))
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
implementation(libs.retrofit.converter.gson)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.core.ktx)
implementation(libs.material)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(libs.androidx.junit)
}
View File
Binary file not shown.
Binary file not shown.
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,24 @@
package com.example.p3_pardakht_novin
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.ps3.test", appContext.packageName)
}
}
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.PRINTER" />
</manifest>
@@ -0,0 +1,78 @@
package com.example.p3_pardakht_novin
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.activity.result.ActivityResult
import com.example.core.model.PaymentResultEntity
import com.example.core.model.PaymentResultStatus
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class P3SepehrIapPosService @Inject constructor(
@param:ApplicationContext private val context: Context
) {
fun isPosAppInstalled(packageName: String = "com.dml.sima7.sepehr"): Boolean {
return try {
context.packageManager.getPackageInfo(packageName, 0)
true
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
fun startPurchase(
amount: Long,
packageName: String = "com.dml.sima7.sepehr"
): Intent {
return Intent("com.dml.sima7.sepehr.activity.Intent_SwipeCardActivity").apply {
setPackage(packageName)
putExtra("amount", amount.toString())
setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
}
fun decodeResponse(
id: String?,
activityResult: ActivityResult
): PaymentResultEntity {
val data = activityResult.data
if (data == null) {
return PaymentResultEntity.failure(
id = id,
message = "پاسخی از دستگاه دریافت نشد!"
)
}
val resultCode = data.getStringExtra("resultCode")
val resultDescription = data.getStringExtra("resultDescription") ?: "خطای نامشخص"
// resultCode "00" usually means success in most Iranian PSPs
val isSuccess = resultCode == "00"
return if (isSuccess) {
PaymentResultEntity(
id = id,
status = PaymentResultStatus.SUCCESS,
paymentResultDataEntity = PaymentResultEntity.PaymentResultDataEntity(
terminalId = data.getStringExtra("terminalNo") ?: "",
stan = data.getStringExtra("traceNo") ?: "",
rrn = data.getStringExtra("refrenceCode") ?: "",
responseCode = resultCode,
customerCardNO = data.getStringExtra("maskedPan") ?: "",
transactionDateTime = data.getStringExtra("DateTime") ?: "",
),
message = resultDescription
)
} else {
PaymentResultEntity.failure(
id = id,
message = resultDescription
)
}
}
}
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import android.content.Context
import android.graphics.Bitmap
@@ -20,7 +20,7 @@ import java.io.File
import javax.inject.Inject
class P3Printer @Inject constructor(
class P3SepehrPrinter @Inject constructor(
@param:ApplicationContext private val context: Context,
) : Printer {
companion object {
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import android.device.PrinterManager
import android.graphics.Bitmap
@@ -16,7 +16,7 @@ import javax.inject.Singleton
* Helper class for interacting with the POS Printer.
*/
@Singleton
class PrinterHelper @Inject constructor() {
class P3SepehrPrinterHelper @Inject constructor() {
private var printer: PrinterManager? = null
private val gson = GsonBuilder().setPrettyPrinting().create()
@@ -1,4 +1,4 @@
package com.example.p3
package com.example.p3_pardakht_novin
import android.content.Intent
import androidx.activity.result.ActivityResult
@@ -8,20 +8,20 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import javax.inject.Inject
class P3Service @Inject constructor(
private val iapPosService: IapPosService,
private val printerHelper: PrinterHelper
class P3SepehrService @Inject constructor(
private val p3SepehrIapPosService: P3SepehrIapPosService,
private val p3SepehrPrinterHelper: P3SepehrPrinterHelper
) : PspService {
private val scope = CoroutineScope(Dispatchers.IO)
override fun pay(amount: Long): Intent {
return iapPosService.startPurchase(amount)
return p3SepehrIapPosService.startPurchase(amount)
}
override fun print(lines: List<String>, onMessage: (String) -> Unit) {
printerHelper.printLines(lines) { status ->
p3SepehrPrinterHelper.printLines(lines) { status ->
onMessage("P3 Printer: $status")
}
}
@@ -32,7 +32,7 @@ class P3Service @Inject constructor(
activityResult: ActivityResult
): PaymentResultEntity {
return try {
iapPosService.decodeResponse(
p3SepehrIapPosService.decodeResponse(
id = id,
activityResult = activityResult
)
@@ -1,7 +1,7 @@
package com.example.p3.di
package com.example.p3_pardakht_novin.di
import android.content.Context
import com.example.p3.IapPosService
import com.example.p3_pardakht_novin.P3SepehrIapPosService
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -11,11 +11,11 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object P3Module {
object P3SepehrModule {
@Provides
@Singleton
fun provideIapPosService(@ApplicationContext context: Context): IapPosService {
return IapPosService(context)
fun provideIapPosService(@ApplicationContext context: Context): P3SepehrIapPosService {
return P3SepehrIapPosService(context)
}
}
@@ -0,0 +1,6 @@
package com.example.p3_pardakht_novin.models
data class IapAccountSplit(
val index: Int,
val percent: Int
)
Binary file not shown.
@@ -0,0 +1,17 @@
package com.example.p3_pardakht_novin
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}