diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1663009b3d98c1f01d4a62e3b77b8ee847155aff..8b02e2e3e2ea6f27fc2722fc04363eae387665ff 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -51,6 +51,7 @@ dependencies {
     implementation("com.squareup.retrofit2:retrofit:2.9.0")
     implementation("com.squareup.retrofit2:converter-gson:2.9.0")
     implementation("androidx.security:security-crypto:1.1.0-alpha06")
+    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1")
     implementation(libs.androidx.work.runtime.ktx)
     testImplementation(libs.junit)
     androidTestImplementation(libs.androidx.junit)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3c98a8ba53033802c3db9594a1fabe427c682126..b53c17d0ec3d5a1e1e3b0544a10f47b17373a663 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools">
+
+    <uses-feature
+        android:name="android.hardware.camera"
+        android:required="false" />
+
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.CAMERA" />
 
     <application
         android:allowBackup="true"
diff --git a/app/src/main/java/com/example/myapplication/backendconnect/BackendService.kt b/app/src/main/java/com/example/myapplication/backendconnect/BackendService.kt
index 39b709bdf2309cb61d0f7637d374c4f12bf2aa17..3d2a0819d409cfafeed7d6f485087855eccaf8c3 100644
--- a/app/src/main/java/com/example/myapplication/backendconnect/BackendService.kt
+++ b/app/src/main/java/com/example/myapplication/backendconnect/BackendService.kt
@@ -2,19 +2,24 @@ package com.example.myapplication.backendconnect
 
 import com.example.myapplication.model.auth.Token
 import com.example.myapplication.model.auth.UserCred
+import com.example.myapplication.model.bill.BillResponse
+import okhttp3.MultipartBody
 import retrofit2.Call
 import retrofit2.http.Body
 import retrofit2.http.Header
+import retrofit2.http.Multipart
 import retrofit2.http.POST
+import retrofit2.http.Part
 
 
 interface BackendService {
     @POST("auth/login")
-    fun login(@Body userCred: UserCred): Call<Token>
+    fun login(@Body data: Map<String, String>): Call<Token>
 
     @POST("auth/token")
-    fun tokenCheck(@Header("Authorization") token : String): Call<Token>
+    fun tokenCheck(@Header("Authorization") token : String): Call<UserCred>
 
-//    @POST("bill/upload")
-//    fun uploadBill(@Body bill: Bill): Call<BillResponse>
+    @Multipart
+    @POST("bill/upload")
+    fun uploadBill(@Header("Authorization") token: String , @Part file: MultipartBody.Part): Call<BillResponse>
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/model/auth/UserCred.kt b/app/src/main/java/com/example/myapplication/model/auth/UserCred.kt
index c46eb7c1d95418954d00efb8464c9b49f265f63d..5fcf2595515bcef81bb54866d97c771c384674e5 100644
--- a/app/src/main/java/com/example/myapplication/model/auth/UserCred.kt
+++ b/app/src/main/java/com/example/myapplication/model/auth/UserCred.kt
@@ -3,8 +3,10 @@ package com.example.myapplication.model.auth
 import com.google.gson.annotations.SerializedName
 
 data class UserCred (
-    @SerializedName("email")
-    val email: String,
-    @SerializedName("password")
-    val password: String
+    @SerializedName("nim")
+    val nim: String,
+    @SerializedName("iat")
+    val iat: String,
+    @SerializedName("exp")
+    val exp: String
     )
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/model/bill/BillResponse.kt b/app/src/main/java/com/example/myapplication/model/bill/BillResponse.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0121273f246c13f23b88512d39803b8e0cc41247
--- /dev/null
+++ b/app/src/main/java/com/example/myapplication/model/bill/BillResponse.kt
@@ -0,0 +1,22 @@
+package com.example.myapplication.model.bill
+
+import com.google.gson.annotations.SerializedName
+
+data class BillResponse (
+    @SerializedName("items")
+    val items: Items
+) {
+    data class Items (
+        @SerializedName("items")
+        val items: List<Item>
+    ) {
+        data class Item (
+            @SerializedName("name")
+            val name: String,
+            @SerializedName("qty")
+            val quantity: Int,
+            @SerializedName("price")
+            val price: Double
+        )
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/repository/AuthRepository.kt b/app/src/main/java/com/example/myapplication/repository/AuthRepository.kt
index 05d37051d471ef2bec300eab66cdc3d2501be94d..f519451746e7fce29eb1fb827a8dc6db7669c8bd 100644
--- a/app/src/main/java/com/example/myapplication/repository/AuthRepository.kt
+++ b/app/src/main/java/com/example/myapplication/repository/AuthRepository.kt
@@ -1,22 +1,32 @@
 package com.example.myapplication.repository
 
+import android.net.Uri
 import android.util.Log
+import androidx.core.content.ContentProviderCompat.requireContext
 import com.example.myapplication.backendconnect.Client
 import com.example.myapplication.model.auth.Token
 import com.example.myapplication.model.auth.UserCred
+import com.example.myapplication.model.bill.BillResponse
 import com.example.myapplication.util.EventBus
 import com.example.myapplication.util.LoginListener
 import com.example.myapplication.util.SecretPreference
+import okhttp3.MediaType
+import okhttp3.MultipartBody
+import okhttp3.RequestBody
+
 import retrofit2.Call
 import retrofit2.Callback
 import retrofit2.Response
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
 
 class AuthRepository (
     private val secretPreference: SecretPreference) {
 
     fun loginRequest(email: String, password: String){
         Log.d("Development", "Login request to backend service")
-        Client.connect.login(UserCred(email, password)).enqueue(
+        Client.connect.login(mapOf("email" to email, "password" to password)).enqueue(
             object : Callback<Token> {
                 override fun onResponse(call: Call<Token>, response: Response<Token>) {
                     Log.d("Development", "Response: ${response.body()}")
@@ -52,8 +62,8 @@ class AuthRepository (
         val token = secretPreference.getToken() ?: ""
 
         Client.connect.tokenCheck("Bearer $token").enqueue(
-            object : Callback<Token> {
-                override fun onResponse(call: Call<Token>, response: Response<Token>) {
+            object : Callback<UserCred> {
+                override fun onResponse(call: Call<UserCred>, response: Response<UserCred>) {
                     Log.d("Development", "Response: ${response.body()}")
                     if (response.isSuccessful) {
                         Log.d("Development", "Token check success: Valid token")
@@ -68,11 +78,40 @@ class AuthRepository (
                     }
                 }
 
-                override fun onFailure(call: Call<Token>, t: Throwable) {
+                override fun onFailure(call: Call<UserCred>, t: Throwable) {
                     Log.d("Development", "TOKEN CHECK FAILED, error on delivery : ${t.message}")
                 }
             }
         )
     }
 
+    fun uploadBillRequest(file: File) {
+        Log.d("Development", "Upload bill request to backend service")
+
+        val token : String = secretPreference.getToken() ?: ""
+        val requestFile = RequestBody.create(MediaType.parse("image/jpeg"), file)
+        val body = MultipartBody.Part.createFormData("file", file.name, requestFile)
+
+        Client.connect.uploadBill("Bearer $token", body).enqueue(
+            object : Callback<BillResponse> {
+                override fun onResponse(call: Call<BillResponse>, response: Response<BillResponse>) {
+                    Log.d("Development", "Response: $response")
+                    if (response.isSuccessful) {
+                        Log.i("Development", "Upload Success, response: ${response.body()}")
+                        EventBus.publish("UPLOAD_SUCCESS")
+                    } else {
+                        Log.d("Development", "UPLOAD FAILED, http code : ${response.code()}")
+                        EventBus.publish("UPLOAD_FAIL")
+                    }
+                }
+
+                override fun onFailure(call: Call<BillResponse>, t: Throwable) {
+                    Log.d("Development", "UPLOAD FAILED, error on delivery : ${t.message}")
+                    EventBus.publish("UPLOAD_FAIL")
+                }
+            }
+        )
+    }
+
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/example/myapplication/ui/dashboard/DashboardFragment.kt b/app/src/main/java/com/example/myapplication/ui/dashboard/DashboardFragment.kt
index e417b48167f5c10e3e10ab5aacd415be5730a1fd..8258269c19efd914decbcc2cf49a0aee3d703cce 100644
--- a/app/src/main/java/com/example/myapplication/ui/dashboard/DashboardFragment.kt
+++ b/app/src/main/java/com/example/myapplication/ui/dashboard/DashboardFragment.kt
@@ -1,22 +1,102 @@
 package com.example.myapplication.ui.dashboard
 
+import android.Manifest
+import android.annotation.SuppressLint
+import android.content.ActivityNotFoundException
+import android.content.ContentValues
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.ImageFormat
+import android.graphics.SurfaceTexture
+import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraDevice
+import android.hardware.camera2.CameraManager
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.params.OutputConfiguration
+import android.hardware.camera2.params.SessionConfiguration
+import android.media.ImageReader
+import android.net.Uri
 import android.os.Bundle
+import android.provider.MediaStore
+import android.util.Log
 import android.view.LayoutInflater
+import android.view.Surface
+import android.view.TextureView
 import android.view.View
 import android.view.ViewGroup
-import android.widget.TextView
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.core.content.ContextCompat
 import androidx.fragment.app.Fragment
 import androidx.lifecycle.ViewModelProvider
-import com.example.myapplication.databinding.FragmentDashboardBinding
+import com.example.myapplication.databinding.FragmentScanBinding
+import com.example.myapplication.repository.AuthRepository
+import com.example.myapplication.util.SecretPreference
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.util.concurrent.Executor
 
 class DashboardFragment : Fragment() {
 
-    private var _binding: FragmentDashboardBinding? = null
+    private var _binding: FragmentScanBinding? = null
 
     // This property is only valid between onCreateView and
     // onDestroyView.
     private val binding get() = _binding!!
 
+    private lateinit var cameraId: String
+    private lateinit var cameraDevice: CameraDevice
+    private lateinit var captureRequestBuilder : CaptureRequest.Builder
+    private lateinit var mainExecutor : Executor
+    private var cameraPermission: Boolean = false
+    private lateinit var fileImage: File
+    private lateinit var imagePath : String
+
+    private lateinit var authRepository : AuthRepository
+
+    private val stateCallBack = object : CameraDevice.StateCallback() {
+        override fun onOpened(camera: CameraDevice) {
+            cameraDevice = camera
+            startCamera()
+        }
+
+        override fun onDisconnected(camera: CameraDevice) {
+            cameraDevice.close()
+        }
+
+        override fun onError(camera: CameraDevice, error: Int) {
+            cameraDevice.close()
+        }
+    }
+
+    private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
+        override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
+            Log.i("Development", "Surface texture available")
+            startCamera()
+        }
+
+        override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
+            // Ignored, Camera does all the work for us
+        }
+
+        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
+            return false
+        }
+
+        override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
+            // Ignored
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        authRepository = AuthRepository(SecretPreference(requireContext()))
+    }
+
     override fun onCreateView(
         inflater: LayoutInflater,
         container: ViewGroup?,
@@ -25,18 +105,216 @@ class DashboardFragment : Fragment() {
         val dashboardViewModel =
             ViewModelProvider(this).get(DashboardViewModel::class.java)
 
-        _binding = FragmentDashboardBinding.inflate(inflater, container, false)
-        val root: View = binding.root
+        _binding = FragmentScanBinding.inflate(inflater, container, false)
 
-        val textView: TextView = binding.textDashboard
-        dashboardViewModel.text.observe(viewLifecycleOwner) {
-            textView.text = it
+        cameraPermissionCheck()
+
+        binding.buttonScan.setOnClickListener {
+            takePicture()
+        }
+
+        binding.buttonSend.setOnClickListener {
+            if(::imagePath.isInitialized) {
+                CoroutineScope(Dispatchers.IO).launch {
+                    val uri = Uri.parse(imagePath)
+                    val parcelFileDescriptor = requireContext().contentResolver.openFileDescriptor(uri, "r")
+                    val file = File.createTempFile("upload", null, requireContext().cacheDir)
+                    val inputStream = FileInputStream(parcelFileDescriptor?.fileDescriptor)
+                    val outputStream = FileOutputStream(file)
+                    inputStream.copyTo(outputStream)
+
+                    authRepository.uploadBillRequest(file)
+
+                    parcelFileDescriptor?.close()
+                }
+            } else {
+                Log.w("Development", "Image path not initialized")
+            }
+        }
+
+        return binding.root
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+
+        fileImage = File(requireContext().externalMediaDirs.first(), "\"${System.currentTimeMillis()}.jpg\"" )
+    }
+
+    override fun onResume() {
+        super.onResume()
+        if(cameraPermission) {
+            setupCamera()
+            binding.cameraPreview.surfaceTextureListener = surfaceTextureListener
         }
-        return root
     }
 
     override fun onDestroyView() {
         super.onDestroyView()
         _binding = null
     }
+
+    // CAMERA
+    private fun cameraPermissionCheck() {
+        Log.i("Development", "Checking camera permission")
+        if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)
+            == PackageManager.PERMISSION_DENIED) {
+            registerForActivityResult(
+                ActivityResultContracts.RequestPermission()
+            ) { isGranted: Boolean ->
+                if (isGranted) {
+                    // Permission is granted
+                    Log.i("Development", "Camera permission granted")
+                    cameraPermission = true
+                } else {
+                    // Permission is denied
+                    Log.w("Development", "Camera permission DENIED")
+                }
+            }.launch(Manifest.permission.CAMERA)
+
+        } else {
+            cameraPermission = true
+        }
+
+        if(cameraPermission) {
+            Log.i("Development", "Camera permission granted")
+        }
+    }
+
+    private fun setupCamera() {
+        Log.i("Development", "Setting up camera")
+        if(ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)
+            == PackageManager.PERMISSION_GRANTED) {
+            val cameraManager =
+                requireContext().getSystemService(Context.CAMERA_SERVICE) as CameraManager
+            cameraId = cameraManager.cameraIdList[0]
+            Log.i("Development", "Camera ID: $cameraId")
+            cameraManager.openCamera(cameraId, stateCallBack, null)
+            Log.i("Development", "Camera opened")
+        }
+    }
+    @SuppressLint("Recycle")
+    private fun startCamera() {
+        val texture = binding.cameraPreview.surfaceTexture
+        if (texture != null) {
+            texture.setDefaultBufferSize(290, 420)
+        } else {
+            Log.e("Development", "Texture is null")
+        }
+        Log.i("Development", "Camera starting")
+
+        val surface = Surface(texture)
+        captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
+        captureRequestBuilder.addTarget(surface)
+
+        cameraDevice.createCaptureSession(listOf(surface), object : CameraCaptureSession.StateCallback() {
+            override fun onConfigured(session: CameraCaptureSession) {
+                session.setRepeatingRequest(captureRequestBuilder.build(), null, null)
+            }
+
+            override fun onConfigureFailed(session: CameraCaptureSession) {
+                Log.w("Development", "Camera configuration failed")
+            }
+        }, null)
+
+//        val outputConfig : OutputConfiguration = OutputConfiguration(surface)
+//        mainExecutor = ContextCompat.getMainExecutor(requireContext())
+//
+//        val sessionConfig : SessionConfiguration = SessionConfiguration(
+//            SessionConfiguration.SESSION_REGULAR,
+//            listOf(outputConfig),
+//            mainExecutor,
+//            cameraCaptureStateCallback)
+//
+//        cameraDevice.createCaptureSession(sessionConfig)
+
+    }
+
+    val REQUEST_IMAGE_CAPTURE = 1
+
+    private fun dispatchTakePictureIntent() {
+        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+        try {
+            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
+        } catch (e: ActivityNotFoundException) {
+            // display error state to the user
+        }
+    }
+
+
+    private fun takePicture() {
+        val captureRequestBuilderTake = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
+        val imageReader = ImageReader.newInstance(290, 420, ImageFormat.JPEG, 1)
+
+        imageReader.setOnImageAvailableListener({ reader ->
+            Log.i("Development", "image listener called")
+            reader.acquireNextImage().use { image ->
+                val buffer = image.planes[0].buffer
+                val bytes = ByteArray(buffer.remaining())
+                buffer.get(bytes)
+                saveImage(bytes)
+                Log.i("Development", "Image saved")
+            }
+        }, null)
+
+
+        captureRequestBuilderTake.addTarget(imageReader.surface)
+        captureRequestBuilderTake.addTarget(Surface(binding.cameraPreview.surfaceTexture))
+
+        val outputConfig = OutputConfiguration(Surface(binding.cameraPreview.surfaceTexture))
+        val outputConfigReader = OutputConfiguration(imageReader.surface)
+
+        val sessionConfig = SessionConfiguration(SessionConfiguration.SESSION_REGULAR,
+            listOf(outputConfig, outputConfigReader), ContextCompat.getMainExecutor(requireContext()), object : CameraCaptureSession.StateCallback() {
+                override fun onConfigured(session: CameraCaptureSession) {
+                    session.capture(captureRequestBuilderTake.build(), null, null)
+                }
+
+                override fun onConfigureFailed(session: CameraCaptureSession) {
+                    Log.e("Development", "Capture session configuration failed")
+                }
+            })
+
+        cameraDevice.createCaptureSession(sessionConfig)
+    }
+
+    private fun saveImage(file: ByteArray) {
+        val resolver = requireContext().contentResolver
+        val contentValues = ContentValues().apply {
+            put(MediaStore.MediaColumns.DISPLAY_NAME, "Image_${System.currentTimeMillis()}.jpg")
+            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+                put(MediaStore.MediaColumns.IS_PENDING, 1)
+            }
+        }
+
+        val uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
+
+        uri?.let {
+            resolver.openOutputStream(it)?.use { outputStream ->
+                outputStream.write(file)
+            }
+
+            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
+                contentValues.clear()
+                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
+                resolver.update(uri, contentValues, null, null)
+
+            }
+            imagePath = it.toString()
+            Log.i("Development", "Image saved to $it")
+        }
+
+    }
+
+
+    val cameraCaptureStateCallback = object : CameraCaptureSession.StateCallback() {
+        override fun onConfigured(session: CameraCaptureSession) {
+            session.setRepeatingRequest(captureRequestBuilder.build(), null, null)
+        }
+
+        override fun onConfigureFailed(session: CameraCaptureSession) {
+            Log.w("Development", "Camera configuration failed")
+        }
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_scan.xml b/app/src/main/res/layout/fragment_scan.xml
new file mode 100644
index 0000000000000000000000000000000000000000..5901b3252192ff780eabb16591041da6ddc2f80b
--- /dev/null
+++ b/app/src/main/res/layout/fragment_scan.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <TextureView
+        android:id="@+id/camera_preview"
+        android:layout_width="290dp"
+        android:layout_height="420dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintVertical_bias="0.311" />
+
+    <Button
+        android:id="@+id/button_scan"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="20dp"
+        android:text="Capture"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.315"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/camera_preview" />
+
+    <Button
+        android:id="@+id/button_send"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Send"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="0.254"
+        app:layout_constraintStart_toEndOf="@id/button_scan"
+        app:layout_constraintTop_toBottomOf="@id/camera_preview"
+        app:layout_constraintVertical_bias="0.12" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file